Compare commits

..

35 Commits

Author SHA1 Message Date
eb65b56b78 convert y axis definition to standalone component 2024-10-01 19:14:15 +02:00
b0415ed972 convert image to to standalone component 2024-10-01 19:09:59 +02:00
4b211e425d convert limit by to standalone component 2024-10-01 19:07:34 +02:00
9459eee606 lazy loading for main page 2024-10-01 18:56:58 +02:00
a81c458775 convert customizable grid to standalone component and add lazy loading 2024-10-01 18:55:24 +02:00
ee0eab22f8 convert help page into standalone component and add lazy loading 2024-10-01 18:47:34 +02:00
9ccb7a14b5 lazy loading for upload page 2024-10-01 18:42:52 +02:00
05cc0e985a convert upload page to standalone component 2024-10-01 18:38:44 +02:00
6dc5ba1a1e convert main page into standalone component 2024-09-29 13:51:06 +02:00
a6ae8e533e convert new dashboard component to standalone component 2024-09-29 13:11:15 +02:00
8e0572d35b convert date picker to standalone component 2024-09-29 10:10:00 +02:00
697d3664aa convert AddTextDialogComponent into standalone component 2024-09-29 09:34:56 +02:00
526e1d842e update Gradle to 8.10.2 2024-09-29 08:59:48 +02:00
06c9b4998f update Junit to 5.11.1 2024-09-29 08:57:48 +02:00
ac5dcdc58f Merge pull request 'dashboard' (#1) from dashboard into master
Reviewed-on: #1
2024-09-29 06:47:35 +00:00
296d42e721 update node to 20.17.0 2024-09-29 08:45:56 +02:00
9db020ceb0 update 3rd party libs 2024-09-29 08:21:46 +02:00
36da503be9 add date picker to dashboard page 2024-09-29 08:11:38 +02:00
42751f84d4 remove head-box dif, because it is unnecessary 2024-09-28 10:35:15 +02:00
39d7c029ea remove debug output for absolute date 2024-09-28 10:28:28 +02:00
f072185074 nicer placement of the date picker 2024-09-28 10:26:53 +02:00
122ba11a79 remove debugger 2024-09-28 10:26:38 +02:00
f1d7799bf1 update luxon to 3.5.0 2024-09-28 09:18:23 +02:00
680f1bff03 update Angular Material to 18.2 2024-09-28 09:16:56 +02:00
fa0315650a update Angular to 18.2 2024-09-28 09:14:31 +02:00
8f765dd478 remove date picker test component 2024-09-28 09:12:31 +02:00
1234560512 update ngx-markdown to be compatible with angular 2024-09-28 08:54:47 +02:00
2711579afb add resource handler to be able to load htm/js
With the Angular update the smart people at google moved the
location where the build output is written to. It is now in a
subfolder called 'browser'. This means the default lookup locations
of Spring don't work anymore.
2024-09-28 08:37:47 +02:00
3ac021e45f ng update @angular/material@18 2024-07-28 12:17:53 +02:00
c199eae4ff g update @angular/core@18 @angular/cli@18 2024-07-28 12:17:00 +02:00
6073dd0779 fix compile errors after angualr 17 update 2024-07-28 12:14:19 +02:00
fee5eda780 update ngx-markdown 2024-07-28 11:58:01 +02:00
cc0db6d732 update angular material to 17 2024-07-28 11:56:46 +02:00
f084396e95 update angular cor and cli to 17 2024-07-28 11:55:22 +02:00
e4b6eea4b1 angular updates are shit 2024-07-28 11:54:09 +02:00
32 changed files with 11946 additions and 8458 deletions

View File

@@ -13,18 +13,18 @@ ext {
javaVersion=21 javaVersion=21
version_log4j2= '2.20.0' // keep in sync with spring-boot-starter-log4j2 version_log4j2= '2.20.0' // keep in sync with spring-boot-starter-log4j2
version_spring = '3.3.2' version_spring = '3.3.4'
version_junit = '5.10.3' version_junit = '5.11.1'
version_junit_platform = '1.10.3' version_junit_platform = '1.11.1'
version_nodejs = '18.18.0' // keep in sync with npm version_nodejs = '20.17.0' // keep in sync with npm
version_npm = '9.8.1' // keep in sync with nodejs version_npm = '10.8.2' // keep in sync with nodejs
lib_antlr = "org.antlr:antlr4:4.13.1" lib_antlr = "org.antlr:antlr4:4.13.2"
lib_commons_collections4 = 'org.apache.commons:commons-collections4:4.4' lib_commons_collections4 = 'org.apache.commons:commons-collections4:4.4'
lib_commons_csv= 'org.apache.commons:commons-csv:1.11.0' lib_commons_csv= 'org.apache.commons:commons-csv:1.12.0'
lib_commons_lang3 = 'org.apache.commons:commons-lang3:3.15.0' lib_commons_lang3 = 'org.apache.commons:commons-lang3:3.17.0'
lib_jackson_databind = 'com.fasterxml.jackson.core:jackson-databind:2.17.2' lib_jackson_databind = 'com.fasterxml.jackson.core:jackson-databind:2.18.0'
lib_log4j2_core = "org.apache.logging.log4j:log4j-core:${version_log4j2}" lib_log4j2_core = "org.apache.logging.log4j:log4j-core:${version_log4j2}"
lib_log4j2_slf4j_impl = "org.apache.logging.log4j:log4j-slf4j-impl:${version_log4j2}" lib_log4j2_slf4j_impl = "org.apache.logging.log4j:log4j-slf4j-impl:${version_log4j2}"
@@ -141,5 +141,5 @@ subprojects {
} }
wrapper { wrapper {
gradleVersion = '8.9' gradleVersion = '8.10.2'
} }

Binary file not shown.

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

@@ -18,12 +18,15 @@
"prefix": "app", "prefix": "app",
"architect": { "architect": {
"build": { "build": {
"builder": "@angular-devkit/build-angular:browser", "builder": "@angular-devkit/build-angular:application",
"options": { "options": {
"outputPath": "build/generated/resources", "outputPath": {
"base": "build/generated/resources"
},
"index": "src/index.html", "index": "src/index.html",
"main": "src/main.ts", "polyfills": [
"polyfills": "src/polyfills.ts", "src/polyfills.ts"
],
"tsConfig": "tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss", "inlineStyleLanguage": "scss",
"assets": [ "assets": [
@@ -35,7 +38,8 @@
], ],
"scripts": [ "scripts": [
"node_modules/marked/marked.min.js" "node_modules/marked/marked.min.js"
] ],
"browser": "src/main.ts"
}, },
"configurations": { "configurations": {
"production": { "production": {
@@ -61,9 +65,7 @@
"sourceMap": true "sourceMap": true
}, },
"development": { "development": {
"buildOptimizer": false,
"optimization": false, "optimization": false,
"vendorChunk": true,
"extractLicenses": false, "extractLicenses": false,
"sourceMap": true, "sourceMap": true,
"namedChunks": true "namedChunks": true
@@ -78,10 +80,10 @@
}, },
"configurations": { "configurations": {
"production": { "production": {
"browserTarget": "pdb-js:build:production" "buildTarget": "pdb-js:build:production"
}, },
"development": { "development": {
"browserTarget": "pdb-js:build:development" "buildTarget": "pdb-js:build:development"
} }
}, },
"defaultConfiguration": "development" "defaultConfiguration": "development"
@@ -89,7 +91,7 @@
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular-devkit/build-angular:extract-i18n",
"options": { "options": {
"browserTarget": "pdb-js:build" "buildTarget": "pdb-js:build"
} }
}, },
"test": { "test": {

19881
pdb-js/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,28 +14,28 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^16.2.7", "@angular/animations": "^18.2.6",
"@angular/cdk": "^16.2.6", "@angular/cdk": "^18.2.6",
"@angular/common": "^16.2.7", "@angular/common": "^18.2.6",
"@angular/compiler": "^16.2.7", "@angular/compiler": "^18.2.6",
"@angular/core": "^16.2.7", "@angular/core": "^18.2.6",
"@angular/forms": "^16.2.7", "@angular/forms": "^18.2.6",
"@angular/material": "^16.2.6", "@angular/material": "^18.2.6",
"@angular/platform-browser": "^16.2.7", "@angular/platform-browser": "^18.2.6",
"@angular/platform-browser-dynamic": "^16.2.7", "@angular/platform-browser-dynamic": "^18.2.6",
"@angular/router": "^16.2.7", "@angular/router": "^18.2.6",
"luxon": "^3.4.3", "luxon": "^3.4.3",
"marked": "^4.2.12", "marked": "^12",
"ngx-markdown": "^16.0.0", "ngx-markdown": "18.0.0",
"rxjs": "~7.5.0", "rxjs": "~7.5.0",
"rxjs-compat": "^6.6.7", "rxjs-compat": "^6.6.7",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.13.3" "zone.js": "^0.14.10"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^16.2.4", "@angular-devkit/build-angular": "^18.2.6",
"@angular/cli": "^16.2.4", "@angular/cli": "^18.2.6",
"@angular/compiler-cli": "^16.2.7", "@angular/compiler-cli": "^18.2.6",
"@types/jasmine": "~4.3.0", "@types/jasmine": "~4.3.0",
"@types/luxon": "^3.3.2", "@types/luxon": "^3.3.2",
"@types/marked": "^4.0.8", "@types/marked": "^4.0.8",
@@ -47,6 +47,6 @@
"karma-jasmine": "~5.1.0", "karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0", "karma-jasmine-html-reporter": "~2.1.0",
"source-map-explorer": "^2.5.3", "source-map-explorer": "^2.5.3",
"typescript": "5.1" "typescript": "^5.4.5"
} }
} }

View File

@@ -1,23 +1,17 @@
import { NgModule } from "@angular/core"; import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router"; import { RouterModule, Routes } from "@angular/router";
import { VisualizationPageComponent } from "./visualization-page/visualization-page.component"; import { VisualizationPageComponent } from "./visualization-page/visualization-page.component";
import { MainPageComponent } from "./main-page/main-page.component";
import { UploadPageComponent } from "./upload-page/upload-page.component";
import { HelpPageComponent } from "./help-page/help-page.component";
import { DashboardPageComponent } from "./dashboard-page/dashboard-page.component"; import { DashboardPageComponent } from "./dashboard-page/dashboard-page.component";
import { DashboardComponent } from "./dashboard-page/dashboard/dashboard.component"; import { DashboardComponent } from "./dashboard-page/dashboard/dashboard.component";
import { CustomizableGridComponent } from "./customizable-grid/customizable-grid.component";
import { DatePickerTestComponent } from "./components/datepicker/date-picker-test.component";
const routes: Routes = [ const routes: Routes = [
{ path: "", component: MainPageComponent }, { path: "", loadComponent: () => import("./main-page/main-page.component").then(m => m.MainPageComponent) },
{ path: "vis", component: VisualizationPageComponent }, { path: "vis", component: VisualizationPageComponent },
{ path: "dashboard", component: DashboardPageComponent }, { path: "dashboard", component: DashboardPageComponent },
{ path: "dashboard/:id", component: DashboardComponent }, { path: "dashboard/:id", component: DashboardComponent },
{ path: "testing/datepicker", component: DatePickerTestComponent }, { path: "upload", loadComponent: () => import("./upload-page/upload-page.component").then(m => m.UploadPageComponent) },
{ path: "upload", component: UploadPageComponent }, { path: "grid", loadComponent: () => import("./customizable-grid/customizable-grid.component").then(m => m.CustomizableGridComponent) },
{ path: "grid", component: CustomizableGridComponent }, { path: "help", loadComponent: () => import("./help-page/help-page.component").then(m => m.HelpPageComponent) },
{ path: "help", component: HelpPageComponent },
// { path: '**', component: PageNotFoundComponent } // { path: '**', component: PageNotFoundComponent }
]; ];

View File

@@ -1,18 +1,14 @@
import { BrowserModule } from "@angular/platform-browser"; import { BrowserModule } from "@angular/platform-browser";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { enableProdMode, NgModule } from "@angular/core"; import { enableProdMode, NgModule } from "@angular/core";
import { HttpClientModule } from "@angular/common/http"; import { provideHttpClient, withInterceptorsFromDi } from "@angular/common/http";
import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { AppRoutingModule } from "./app-routing.module"; import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component"; import { AppComponent } from "./app.component";
import { MainPageComponent } from "./main-page/main-page.component";
import { HelpPageComponent } from "./help-page/help-page.component";
import { UploadPageComponent } from "./upload-page/upload-page.component";
import { VisualizationPageComponent } from "./visualization-page/visualization-page.component"; import { VisualizationPageComponent } from "./visualization-page/visualization-page.component";
import { DatePickerComponent } from "./components/datepicker/date-picker.component"; import { DatePickerComponent } from "./components/datepicker/date-picker.component";
import { DatePickerTestComponent } from "./components/datepicker/date-picker-test.component";
import { MatAutocompleteModule } from "@angular/material/autocomplete"; import { MatAutocompleteModule } from "@angular/material/autocomplete";
import { MatButtonModule } from "@angular/material/button"; import { MatButtonModule } from "@angular/material/button";
@@ -28,7 +24,6 @@ import { MatTooltipModule } from "@angular/material/tooltip";
import { OverlayModule } from "@angular/cdk/overlay"; import { OverlayModule } from "@angular/cdk/overlay";
import { YAxisDefinitionComponent } from "./y-axis-definition/y-axis-definition.component"; import { YAxisDefinitionComponent } from "./y-axis-definition/y-axis-definition.component";
import { QueryAutocompleteComponent } from "./query-autocomplete/query-autocomplete.component"; import { QueryAutocompleteComponent } from "./query-autocomplete/query-autocomplete.component";
import { LimitByComponent } from "./limit-by/limit-by.component";
import { PlotDetailsComponent } from "./plot-details/plot-details.component"; import { PlotDetailsComponent } from "./plot-details/plot-details.component";
import { PlotViewComponent } from "./plot-view/plot-view.component"; import { PlotViewComponent } from "./plot-view/plot-view.component";
import { import {
@@ -38,7 +33,6 @@ import {
} from "./gallery-view/gallery-view.component"; } from "./gallery-view/gallery-view.component";
import { ImageToggleComponent } from "./image-toggle/image-toggle.component"; import { ImageToggleComponent } from "./image-toggle/image-toggle.component";
import { DashboardPageComponent } from "./dashboard-page/dashboard-page.component"; import { DashboardPageComponent } from "./dashboard-page/dashboard-page.component";
import { NewDashboardComponent } from "./dashboard-page/new-dashboard/new-dashboard.component";
import { import {
MAT_DIALOG_DEFAULT_OPTIONS, MAT_DIALOG_DEFAULT_OPTIONS,
MatDialogModule, MatDialogModule,
@@ -49,82 +43,73 @@ import { MatGridListModule } from "@angular/material/grid-list";
import { MatCardModule } from "@angular/material/card"; import { MatCardModule } from "@angular/material/card";
import { MatBadgeModule } from "@angular/material/badge"; import { MatBadgeModule } from "@angular/material/badge";
import { DashboardComponent } from "./dashboard-page/dashboard/dashboard.component"; import { DashboardComponent } from "./dashboard-page/dashboard/dashboard.component";
import { AddTextDialogComponent } from "./dashboard-page/dashboard/add-text-dialog/add-text-dialog.component";
import { TextWidgetComponent } from "./dashboard-page/dashboard/text-widget/text-widget.component"; import { TextWidgetComponent } from "./dashboard-page/dashboard/text-widget/text-widget.component";
import { AddPlotDialogComponent } from "./dashboard-page/dashboard/add-plot-dialog/add-plot-dialog.component"; import { AddPlotDialogComponent } from "./dashboard-page/dashboard/add-plot-dialog/add-plot-dialog.component";
import { PlotWidgetComponent } from "./dashboard-page/dashboard/plot-widget/plot-widget.component"; import { PlotWidgetComponent } from "./dashboard-page/dashboard/plot-widget/plot-widget.component";
import { FullScreenPlotDialogComponent } from "./dashboard-page/dashboard/full-screen-plot-dialog/full-screen-plot-dialog.component"; import { FullScreenPlotDialogComponent } from "./dashboard-page/dashboard/full-screen-plot-dialog/full-screen-plot-dialog.component";
import { CustomizableGridComponent } from "./customizable-grid/customizable-grid.component";
import { DragDropModule } from "@angular/cdk/drag-drop"; import { DragDropModule } from "@angular/cdk/drag-drop";
import { ConfirmationDialogComponent } from "./confirmation-dialog/confirmation-dialog.component"; import { ConfirmationDialogComponent } from "./confirmation-dialog/confirmation-dialog.component";
import { FocusDirective } from "./focus.directive"; import { FocusDirective } from "./focus.directive";
import { MarkdownModule } from "ngx-markdown"; import { MarkdownModule } from "ngx-markdown";
import { MainPageComponent } from "./main-page/main-page.component";
import { LimitByComponent } from "./limit-by/limit-by.component";
@NgModule({ @NgModule({ declarations: [
declarations: [ AppComponent,
AppComponent, VisualizationPageComponent,
MainPageComponent, QueryAutocompleteComponent,
HelpPageComponent, PlotDetailsComponent,
UploadPageComponent, PlotViewComponent,
DatePickerComponent, GalleryViewComponent,
DatePickerTestComponent, GalleryItemView,
VisualizationPageComponent, GalleryFilterView,
YAxisDefinitionComponent, DashboardPageComponent,
QueryAutocompleteComponent, DashboardComponent,
LimitByComponent, TextWidgetComponent,
PlotDetailsComponent, AddPlotDialogComponent,
PlotViewComponent, PlotWidgetComponent,
GalleryViewComponent, FullScreenPlotDialogComponent,
GalleryItemView, ConfirmationDialogComponent,
GalleryFilterView, FocusDirective,
ImageToggleComponent, ],
DashboardPageComponent, bootstrap: [AppComponent],
NewDashboardComponent, imports: [
DashboardComponent, MarkdownModule.forRoot(),
AddTextDialogComponent, BrowserModule,
TextWidgetComponent, AppRoutingModule,
AddPlotDialogComponent, FormsModule,
PlotWidgetComponent, ReactiveFormsModule,
FullScreenPlotDialogComponent, DatePickerComponent,
CustomizableGridComponent, DragDropModule,
ConfirmationDialogComponent, ImageToggleComponent,
FocusDirective, LimitByComponent,
], MainPageComponent,
imports: [ MatAutocompleteModule,
MarkdownModule.forRoot(), MatBadgeModule,
BrowserModule, MatButtonModule,
AppRoutingModule, MatCardModule,
FormsModule, MatCheckboxModule,
ReactiveFormsModule, MatDialogModule,
DragDropModule, MatFormFieldModule,
MatAutocompleteModule, MatGridListModule,
MatBadgeModule, MatInputModule,
MatButtonModule, MatRadioModule,
MatCardModule, MatProgressBarModule,
MatCheckboxModule, MatProgressSpinnerModule,
MatDialogModule, MatSelectModule,
MatFormFieldModule, MatSnackBarModule,
MatGridListModule, MatTabsModule,
MatInputModule, MatTableModule,
MatRadioModule, MatTooltipModule,
MatProgressBarModule, BrowserAnimationsModule,
MatProgressSpinnerModule, OverlayModule,
MatSelectModule, YAxisDefinitionComponent
MatSnackBarModule, ],
MatTabsModule, providers: [{
MatTableModule, provide: MAT_DIALOG_DEFAULT_OPTIONS,
MatTooltipModule, useValue: { hasBackdrop: true },
BrowserAnimationsModule, }, provideHttpClient(withInterceptorsFromDi())] })
HttpClientModule,
OverlayModule,
],
providers: [{
provide: MAT_DIALOG_DEFAULT_OPTIONS,
useValue: { hasBackdrop: true },
}],
bootstrap: [AppComponent],
})
export class AppModule {} export class AppModule {}
enableProdMode(); enableProdMode();

View File

@@ -1,15 +0,0 @@
<style></style>
<form>
<app-date-picker
[formControl]="datePicker"
(dateValueSelected)="(dateChanged)"
></app-date-picker>
Type: {{ datePicker.value?.type }}<br />
value: {{ datePicker.value?.value }}<br />
display: {{ datePicker.value?.display }}<br />
</form>
<button (click)="readValue()">read value</button>
<div>{{ output }}</div>

View File

@@ -1,29 +0,0 @@
import { Component } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";
import { DateValue } from "./date-picker.component";
@Component({
selector: "app-date-picker-test",
templateUrl: "./date-picker-test.component.html",
})
export class DatePickerTestComponent {
datePicker = new FormControl<DateValue>(
new DateValue(
"ABSOLUTE",
"2019-10-05 12:34:56 - 2019-10-11 23:59:59",
"2019-10-05 12:34:56 - 2019-10-11 23:59:59",
),
);
output = "";
readValue() {
this.output = this.datePicker.value?.type + " " +
this.datePicker.value?.value;
}
dateChanged(e: any) {
console.log("dateChanged", e);
this.output += "dateChanged: " + e;
}
}

View File

@@ -1,13 +1,4 @@
<style> <style>
#date-box {
width: 23.5em;
}
.header-box {
display: flex;
justify-content: end;
}
.date-picker-overlay { .date-picker-overlay {
width: 500px; width: 500px;
transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1);
@@ -31,18 +22,16 @@
} }
</style> </style>
<div class="header-box"> <button
<button mat-button
mat-button matTooltip="Date Picker"
matTooltip="Date Picker" (click)="isOpen = !isOpen"
(click)="isOpen = !isOpen" cdkOverlayOrigin
cdkOverlayOrigin #trigger="cdkOverlayOrigin"
#trigger="cdkOverlayOrigin" [attr.disabled]="isDisabled ? 'disabled' : null"
[attr.disabled]="isDisabled ? 'disabled' : null" >
> {{ datePickerControl.value?.display }}
{{ datePickerControl.value?.display }} </button>
</button>
</div>
<ng-template <ng-template
cdkConnectedOverlay cdkConnectedOverlay
@@ -193,8 +182,6 @@
<input matInput [formControl]="dateRange" name="dates" /> <input matInput [formControl]="dateRange" name="dates" />
</mat-form-field> </mat-form-field>
<button mat-button (click)="applyAbsoluteTime()">Apply</button> <button mat-button (click)="applyAbsoluteTime()">Apply</button>
<p>Value: {{ dateRange.value }}</p>
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>
</div> </div>

View File

@@ -1,3 +1,4 @@
import { OverlayModule } from "@angular/cdk/overlay";
import { import {
Component, Component,
EventEmitter, EventEmitter,
@@ -8,9 +9,18 @@ import {
import { import {
ControlValueAccessor, ControlValueAccessor,
FormControl, FormControl,
FormsModule,
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR,
ReactiveFormsModule,
Validators, Validators,
} from "@angular/forms"; } from "@angular/forms";
import { MatButtonModule } from "@angular/material/button";
import { MAT_DIALOG_DEFAULT_OPTIONS } from "@angular/material/dialog";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatInputModule } from "@angular/material/input";
import { MatTabsModule } from "@angular/material/tabs";
import { BrowserModule } from "@angular/platform-browser";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
export type DateType = "QUICK" | "RELATIVE" | "ABSOLUTE"; export type DateType = "QUICK" | "RELATIVE" | "ABSOLUTE";
@@ -29,12 +39,27 @@ export class DatePickerChange {
@Component({ @Component({
selector: "app-date-picker", selector: "app-date-picker",
templateUrl: "./date-picker.component.html", templateUrl: "./date-picker.component.html",
standalone: true,
imports: [
BrowserModule,
MatButtonModule,
MatFormFieldModule,
MatInputModule,
MatTabsModule,
FormsModule,
ReactiveFormsModule,
OverlayModule
],
providers: [ providers: [
{ {
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DatePickerComponent), useExisting: forwardRef(() => DatePickerComponent),
multi: true, multi: true,
}, },
{
provide: MAT_DIALOG_DEFAULT_OPTIONS,
useValue: { hasBackdrop: true },
}
], ],
}) })
export class DatePickerComponent implements ControlValueAccessor { export class DatePickerComponent implements ControlValueAccessor {

View File

@@ -1,12 +1,18 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { CdkDragEnter, CdkDropList, moveItemInArray, DragRef} from '@angular/cdk/drag-drop'; import { CdkDragEnter, CdkDropList, moveItemInArray, DragRef, DragDropModule} from '@angular/cdk/drag-drop';
import { AfterViewInit } from '@angular/core'; import { AfterViewInit } from '@angular/core';
import { ViewChild } from '@angular/core'; import { ViewChild } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@Component({ @Component({
selector: 'app-customizable-grid', selector: 'app-customizable-grid',
templateUrl: './customizable-grid.component.html' templateUrl: './customizable-grid.component.html',
standalone: true,
imports: [
BrowserModule,
DragDropModule
]
}) })
export class CustomizableGridComponent implements AfterViewInit { export class CustomizableGridComponent implements AfterViewInit {
@ViewChild(CdkDropList) placeholder!: CdkDropList; @ViewChild(CdkDropList) placeholder!: CdkDropList;

View File

@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, Inject, ViewChild } from '@angular/core'; import { Component, Inject, ViewChild } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { PlotConfig } from 'src/app/plot.service'; import { PlotConfig } from 'src/app/plot.service';
import { VisualizationPageComponent } from 'src/app/visualization-page/visualization-page.component'; import { VisualizationPageComponent } from 'src/app/visualization-page/visualization-page.component';

View File

@@ -1,9 +1,28 @@
import { Component, ElementRef, Inject, ViewChild } from '@angular/core'; import { Component, ElementRef, Inject, ViewChild } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MarkdownModule } from 'ngx-markdown';
@Component({ @Component({
selector: 'app-add-text-dialog', selector: 'app-add-text-dialog',
templateUrl: './add-text-dialog.component.html' templateUrl: './add-text-dialog.component.html',
standalone: true,
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
ReactiveFormsModule,
MarkdownModule,
MatButtonModule,
MatDialogModule,
MatFormFieldModule,
MatInputModule,
]
}) })
export class AddTextDialogComponent { export class AddTextDialogComponent {
text = ""; text = "";

View File

@@ -3,6 +3,16 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.toolbar {
display: flex;
flex-direction: row;
}
.toolbar #filter-date-range{
flex-grow: 1;
display: flex;
align-items: center;
justify-content: flex-end;
}
.center { .center {
position: absolute; position: absolute;
top: 50%; top: 50%;
@@ -37,6 +47,9 @@
flex-basis: 0; flex-basis: 0;
} }
.editable {
padding: 0.5em;
}
.editable-hovered { .editable-hovered {
visibility: hidden; visibility: hidden;
@@ -73,12 +86,14 @@
<button mat-button (click)="addText()">Add Text</button> <button mat-button (click)="addText()">Add Text</button>
<button mat-button (click)="addPlot()">Add Plot</button> <button mat-button (click)="addPlot()">Add Plot</button>
<button class="save-button" mat-button (click)="save()" [disabled]="!isDirty()">Save</button> <button class="save-button" mat-button (click)="save()" [disabled]="!isDirty()">Save</button>
<div id="filter-date-range">
Date range: <app-date-picker #datePicker (dateValueSelected)="updateDateRange($event)" ></app-date-picker>
</div>
</div> </div>
<div class="editable"> <div class="editable">
<h1>{{dashboard.name}}<button mat-icon-button (click)="editNameAndDescription()" class="editable-hovered"><img src="/assets/img/edit-outline.svg"/></button></h1> <h1>{{dashboard.name}}<button mat-icon-button (click)="editNameAndDescription()" class="editable-hovered"><img src="/assets/img/edit-outline.svg"/></button></h1>
<p>{{dashboard.description}}</p> <p>{{dashboard.description}}</p>
</div> </div>
<div cdkDropListGroup class="dashboard-area"> <div cdkDropListGroup class="dashboard-area">
<div <div
cdkDropList cdkDropList

View File

@@ -1,6 +1,6 @@
import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
import { HttpErrorResponse } from '@angular/common/http'; import { HttpErrorResponse } from '@angular/common/http';
import { Component, ElementRef, OnInit } from '@angular/core'; import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
@@ -9,12 +9,13 @@ import { PlotConfig, PlotResponse, PlotService } from 'src/app/plot.service';
import { NewDashboardComponent } from '../new-dashboard/new-dashboard.component'; import { NewDashboardComponent } from '../new-dashboard/new-dashboard.component';
import { AddPlotDialogComponent } from './add-plot-dialog/add-plot-dialog.component'; import { AddPlotDialogComponent } from './add-plot-dialog/add-plot-dialog.component';
import { AddTextDialogComponent } from './add-text-dialog/add-text-dialog.component'; import { AddTextDialogComponent } from './add-text-dialog/add-text-dialog.component';
import { DatePickerChange, DatePickerComponent } from 'src/app/components/datepicker/date-picker.component';
@Component({ @Component({
selector: 'app-dashboard', selector: 'app-dashboard',
templateUrl: './dashboard.component.html' templateUrl: './dashboard.component.html'
}) })
export class DashboardComponent implements OnInit { export class DashboardComponent implements OnInit{
dashboard?: Dashboard = undefined; dashboard?: Dashboard = undefined;
@@ -24,6 +25,9 @@ export class DashboardComponent implements OnInit {
plotWidgetRenderData: PlotWidgetRenderData[] = []; plotWidgetRenderData: PlotWidgetRenderData[] = [];
@ViewChild("datePicker")
datePicker!: DatePickerComponent;
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private service: DashboardService, private service: DashboardService,
@@ -58,6 +62,13 @@ export class DashboardComponent implements OnInit {
}); });
} }
updateDateRange(e: DatePickerChange) {
this.plotWidgetRenderData.forEach(r => {
r.widget.config.dateRange = e.value;
});
this.loadImages(0, this.plotWidgetRenderData);
}
isDirty() { isDirty() {
return this.pristineDashboardJSON !== JSON.stringify(this.dashboard); return this.pristineDashboardJSON !== JSON.stringify(this.dashboard);
} }
@@ -70,10 +81,13 @@ export class DashboardComponent implements OnInit {
if (plot.isAborted) { if (plot.isAborted) {
this.loadImages(index +1 , plotWidgetQueue); this.loadImages(index +1 , plotWidgetQueue);
}else{ }else{
plot.plotResponse = undefined; // remove old image and show loading icon
const request = PlotWidget.createPlotRequest(plot.widget, plot.submitterId); const request = PlotWidget.createPlotRequest(plot.widget, plot.submitterId);
this.plotService.sendPlotRequest(request).subscribe({ this.plotService.sendPlotRequest(request).subscribe({
next: (response: PlotResponse)=> { next: (response: PlotResponse)=> {
plot.plotResponse= response; plot.plotResponse = response;
}, },
error: (error:any)=> { error: (error:any)=> {
plot.error = error; plot.error = error;

View File

@@ -1,4 +1,4 @@
import { AfterViewInit, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { Component, EventEmitter, Input, Output, ViewChild, input } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { ConfirmationDialogComponent } from 'src/app/confirmation-dialog/confirmation-dialog.component'; import { ConfirmationDialogComponent } from 'src/app/confirmation-dialog/confirmation-dialog.component';
import { PlotWidget, PlotWidgetRenderData } from 'src/app/dashboard.service'; import { PlotWidget, PlotWidgetRenderData } from 'src/app/dashboard.service';
@@ -18,14 +18,13 @@ export class PlotWidgetComponent {
public thumbnailUrl = ""; public thumbnailUrl = "";
@ViewChild("plotView") plotView!: PlotViewComponent; //@ViewChild("plotView") plotView!: PlotViewComponent;
@Output() @Output()
deleted : EventEmitter<string> = new EventEmitter<string>(); deleted : EventEmitter<string> = new EventEmitter<string>();
constructor(private dialog: MatDialog, private service: PlotService){} constructor(private dialog: MatDialog, private service: PlotService){}
hasRender(name: string): boolean{ hasRender(name: string): boolean{
return this.data !== undefined && this.data.plotResponse !== undefined && this.data.plotResponse?.rendered[name] !== undefined; return this.data !== undefined && this.data.plotResponse !== undefined && this.data.plotResponse?.rendered[name] !== undefined;
} }

View File

@@ -1,11 +1,26 @@
import { Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core'; import { Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from '@angular/material/dialog';
import { DashboardCreationData } from 'src/app/dashboard.service'; import { DashboardCreationData } from 'src/app/dashboard.service';
import { AbstractControl, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { MatButtonModule } from '@angular/material/button';
import { OverlayModule } from '@angular/cdk/overlay';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
@Component({ @Component({
selector: 'app-new-dashboard', selector: 'app-new-dashboard',
templateUrl: './new-dashboard.component.html' templateUrl: './new-dashboard.component.html',
standalone: true,
imports: [
BrowserModule,
FormsModule,
MatButtonModule,
MatDialogModule,
MatFormFieldModule,
MatInputModule,
ReactiveFormsModule
]
}) })
export class NewDashboardComponent implements OnInit { export class NewDashboardComponent implements OnInit {

View File

@@ -3,7 +3,8 @@ import { Component, OnInit } from '@angular/core';
@Component({ @Component({
selector: 'pdb-help-page', selector: 'pdb-help-page',
templateUrl: './help-page.component.html', templateUrl: './help-page.component.html',
styleUrls: ['./help-page.component.scss'] styleUrls: ['./help-page.component.scss'],
standalone: true
}) })
export class HelpPageComponent implements OnInit { export class HelpPageComponent implements OnInit {

View File

@@ -1,9 +1,14 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@Component({ @Component({
selector: 'pdb-image-toggle', selector: 'pdb-image-toggle',
templateUrl: './image-toggle.component.html', templateUrl: './image-toggle.component.html',
styleUrls: ['./image-toggle.component.scss'] styleUrls: ['./image-toggle.component.scss'],
standalone: true,
imports: [
BrowserModule
]
}) })
export class ImageToggleComponent implements OnInit { export class ImageToggleComponent implements OnInit {

View File

@@ -1,10 +1,25 @@
import { Component, Input} from '@angular/core'; import { Component } from '@angular/core';
import {FormControl} from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatFormField, MatLabel } from '@angular/material/form-field';
import { MatInput } from '@angular/material/input';
import { MatOption, MatSelect } from '@angular/material/select';
import { BrowserModule } from '@angular/platform-browser';
@Component({ @Component({
selector: 'pdb-limit-by', selector: 'pdb-limit-by',
templateUrl: './limit-by.component.html', templateUrl: './limit-by.component.html',
styleUrls: ['./limit-by.component.scss'] styleUrls: ['./limit-by.component.scss'],
standalone: true,
imports: [
BrowserModule,
FormsModule,
MatFormField,
MatInput,
MatLabel,
MatSelect,
MatOption,
ReactiveFormsModule
]
}) })
export class LimitByComponent { export class LimitByComponent {

View File

@@ -1,15 +1,14 @@
import { Component, OnInit } from '@angular/core'; import { Component } from '@angular/core';
import { RouterLink } from '@angular/router';
@Component({ @Component({
selector: 'pdb-main-page', selector: 'pdb-main-page',
templateUrl: './main-page.component.html', templateUrl: './main-page.component.html',
styleUrls: ['./main-page.component.scss'] standalone: true,
imports: [
RouterLink
]
}) })
export class MainPageComponent implements OnInit { export class MainPageComponent {
constructor() { }
ngOnInit() {
}
} }

View File

@@ -1,4 +1,4 @@
import { Component, OnInit, Input, Output, ViewChild, EventEmitter, ɵpublishDefaultGlobalUtils } from '@angular/core'; import { Component, Input } from '@angular/core';
import { DashTypeAndColor, PlotResponseStats, DataSeriesStats } from '../plot.service'; import { DashTypeAndColor, PlotResponseStats, DataSeriesStats } from '../plot.service';
import { UtilService } from '../utils.service'; import { UtilService } from '../utils.service';

View File

@@ -298,7 +298,6 @@ export class PlotViewComponent {
*/ */
shiftDateByAnchor(dateValue:DateValue, anchorInPercentOfDateRange:number, zoomFactor:number) shiftDateByAnchor(dateValue:DateValue, anchorInPercentOfDateRange:number, zoomFactor:number)
{ {
debugger;
const dateRangeParsed = this.parseDateRange(dateValue); const dateRangeParsed = this.parseDateRange(dateValue);
dateRangeParsed.subscribe({ dateRangeParsed.subscribe({
next: (dataRange: DateRange) => { next: (dataRange: DateRange) => {
@@ -330,7 +329,6 @@ export class PlotViewComponent {
*/ */
shiftDate(dateValue: DateValue, factorStartDate: number, factorEndDate: number) shiftDate(dateValue: DateValue, factorStartDate: number, factorEndDate: number)
{ {
debugger;
this.parseDateRange(dateValue).subscribe( this.parseDateRange(dateValue).subscribe(
dateRangeParsed => { dateRangeParsed => {
const dateRangeInSeconds = Math.floor(dateRangeParsed.duration.toMillis()/1000); const dateRangeInSeconds = Math.floor(dateRangeParsed.duration.toMillis()/1000);

View File

@@ -3,7 +3,8 @@ import { Component, OnInit } from '@angular/core';
@Component({ @Component({
selector: 'pdb-upload-page', selector: 'pdb-upload-page',
templateUrl: './upload-page.component.html', templateUrl: './upload-page.component.html',
styleUrls: ['./upload-page.component.scss'] styleUrls: ['./upload-page.component.scss'],
standalone: true
}) })
export class UploadPageComponent implements OnInit { export class UploadPageComponent implements OnInit {

View File

@@ -15,7 +15,7 @@
grid: grid:
"query-box query-box date-box" auto "query-box query-box date-box" auto
"filters results results" 1fr "filters results results" 1fr
/ 25.5em 3fr 23.5em; / 25.5em 3fr auto;
} }
} }
@@ -43,6 +43,8 @@
#date-box{ #date-box{
grid-area: date-box; grid-area: date-box;
display: flex;
align-items: center;
} }

View File

@@ -1,10 +1,27 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { YAxisDefinition } from '../plot.service'; import { YAxisDefinition } from '../plot.service';
import { BrowserModule } from '@angular/platform-browser';
import { MatFormField, MatLabel } from '@angular/material/form-field';
import { MatOptgroup, MatOption, MatSelect } from '@angular/material/select';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatInput } from '@angular/material/input';
@Component({ @Component({
selector: 'pdb-y-axis-definition', selector: 'pdb-y-axis-definition',
templateUrl: './y-axis-definition.component.html', templateUrl: './y-axis-definition.component.html',
styleUrls: ['./y-axis-definition.component.scss'] styleUrls: ['./y-axis-definition.component.scss'],
standalone: true,
imports: [
BrowserModule,
FormsModule,
MatFormField,
MatInput,
MatLabel,
MatSelect,
MatOption,
MatOptgroup,
ReactiveFormsModule
]
}) })
export class YAxisDefinitionComponent { export class YAxisDefinitionComponent {

View File

@@ -14,25 +14,21 @@
// If you don't need the default component typographies but still want the hierarchy styles, // If you don't need the default component typographies but still want the hierarchy styles,
// you can delete this line and instead use: // you can delete this line and instead use:
// `@include mat.legacy-typography-hierarchy(mat.define-typography-config());` // `@include mat.legacy-typography-hierarchy(mat.define-typography-config());`
/* TODO(mdc-migration): Remove all-legacy-component-typographies once all legacy components are migrated*/
@include mat.all-legacy-component-typographies();
@include mat.all-component-typographies(); @include mat.all-component-typographies();
/* TODO(mdc-migration): Remove legacy-core once all legacy components are migrated*/
@include mat.legacy-core();
@include mat.core(); @include mat.core();
// Define the palettes for your theme using the Material Design palettes available in palette.scss // Define the palettes for your theme using the Material Design palettes available in palette.scss
// (imported above). For each palette, you can optionally specify a default, lighter, and darker // (imported above). For each palette, you can optionally specify a default, lighter, and darker
// hue. Available color palettes: https://material.io/design/color/ // hue. Available color palettes: https://material.io/design/color/
$candy-app-primary: mat.define-palette(mat.$blue-palette); $candy-app-primary: mat.m2-define-palette(mat.$m2-blue-palette);
$candy-app-accent: mat.define-palette(mat.$blue-palette, A200, A100, A400); $candy-app-accent: mat.m2-define-palette(mat.$m2-blue-palette, A200, A100, A400);
// The warn palette is optional (defaults to red). // The warn palette is optional (defaults to red).
$candy-app-warn: mat.define-palette(mat.$red-palette); $candy-app-warn: mat.m2-define-palette(mat.$m2-red-palette);
// Create the theme object. A theme consists of configurations for individual // Create the theme object. A theme consists of configurations for individual
// theming systems such as "color" or "typography". // theming systems such as "color" or "typography".
$candy-app-theme: mat.define-light-theme(( $candy-app-theme: mat.m2-define-light-theme((
color: ( color: (
primary: $candy-app-primary, primary: $candy-app-primary,
accent: $candy-app-accent, accent: $candy-app-accent,
@@ -45,8 +41,6 @@ $candy-app-theme: mat.define-light-theme((
// Include theme styles for core and each component used in your app. // Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component // Alternatively, you can import and @include the theme mixins for each component
// that you are using. // that you are using.
/* TODO(mdc-migration): Remove all-legacy-component-themes once all legacy components are migrated*/
@include mat.all-legacy-component-themes($candy-app-theme);
@include mat.all-component-themes($candy-app-theme); @include mat.all-component-themes($candy-app-theme);
@@ -204,7 +198,7 @@ app-add-text-dialog .mat-mdc-form-field-subscript-wrapper {
.errorPanel { .errorPanel {
padding: 1ex; padding: 1ex;
background-color: map-get(mat.$red-palette, 100); background-color: map-get(mat.$m2-red-palette, 100);
border-radius: 5px; border-radius: 5px;
} }

View File

@@ -5,6 +5,7 @@
"baseUrl": "./", "baseUrl": "./",
"outDir": "./dist/out-tsc", "outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
"strict": true, "strict": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": true,
@@ -12,7 +13,6 @@
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"sourceMap": true, "sourceMap": true,
"declaration": false, "declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"moduleResolution": "node", "moduleResolution": "node",
"importHelpers": true, "importHelpers": true,

View File

@@ -26,7 +26,7 @@ public class WebConfiguration implements WebMvcConfigurer, HardcodedValues, Prop
addResourceHandlerForPlottedImages(registry); addResourceHandlerForPlottedImages(registry);
// addResourceHandlerForAngular(registry); addResourceHandlerForAngular(registry);
} }
private void addResourceHandlerForPlottedImages(final ResourceHandlerRegistry registry) { private void addResourceHandlerForPlottedImages(final ResourceHandlerRegistry registry) {
@@ -57,7 +57,7 @@ public class WebConfiguration implements WebMvcConfigurer, HardcodedValues, Prop
// to determine which sub-page to show. // to determine which sub-page to show.
// //
// This makes Angular also responsible for all 404 pages. // This makes Angular also responsible for all 404 pages.
registry.addResourceHandler("/**").addResourceLocations("classpath:/resources/").resourceChain(true) registry.addResourceHandler("/**").addResourceLocations("classpath:/resources/browser/").resourceChain(true)
.addResolver(new PathResourceResolver() { .addResolver(new PathResourceResolver() {
@Override @Override
protected Resource getResource(final String resourcePath, final Resource location) protected Resource getResource(final String resourcePath, final Resource location)
@@ -65,7 +65,7 @@ public class WebConfiguration implements WebMvcConfigurer, HardcodedValues, Prop
final Resource requestedResource = location.createRelative(resourcePath); final Resource requestedResource = location.createRelative(resourcePath);
return requestedResource.exists() && requestedResource.isReadable() ? requestedResource return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
: new ClassPathResource("/resources/index.html"); : new ClassPathResource("/resources/browser/index.html");
} }
}); });
} }