diff --git a/client/app/main.ts b/client/app/main.ts index 0f53d4b33e..20a42ff1ae 100644 --- a/client/app/main.ts +++ b/client/app/main.ts @@ -1,3 +1,14 @@ + + +const translationModule = TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: createTranslateLoader, + deps: [HttpClient], + }, + }) +; + // https://github.com/globaleaks/GlobaLeaks/issues/3277 // Create a proxy to override localStorage methods with sessionStorage methods (function() { @@ -20,8 +31,51 @@ }); })(); -import {platformBrowserDynamic} from "@angular/platform-browser-dynamic"; -import {AppModule} from "@app/app.module"; +import { ReceiptValidatorDirective } from "@app/shared/directive/receipt-validator.directive"; +import { mockEngine } from "./src/services/helper/mocks"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateService, TranslateModule, TranslateLoader } from "@ngx-translate/core"; +import { HTTP_INTERCEPTORS, withInterceptorsFromDi, provideHttpClient, HttpClient } from "@angular/common/http"; +import { appInterceptor, ErrorCatchingInterceptor, CompletedInterceptor } from "@app/services/root/app-interceptor.service"; +import { APP_BASE_HREF, LocationStrategy, HashLocationStrategy, NgOptimizedImage } from "@angular/common"; +import { FlowInjectionToken, NgxFlowModule } from "@flowjs/ngx-flow"; +import { NgbDatepickerI18n, NgbModule } from "@ng-bootstrap/ng-bootstrap"; +import { CustomDatepickerI18n } from "@app/shared/services/custom-datepicker-i18n"; +import { appRoutes } from "@app/app.routes"; +import { BrowserModule, bootstrapApplication } from "@angular/platform-browser"; +import { provideAnimations } from "@angular/platform-browser/animations"; +import { NgSelectModule } from "@ng-select/ng-select"; +import { FormsModule } from "@angular/forms"; +import { NgIdleKeepaliveModule } from "@ng-idle/keepalive"; +import { MarkdownModule, MARKED_OPTIONS } from "ngx-markdown"; +import { AppComponent, createTranslateLoader } from "./src/pages/app/app.component"; +import { importProvidersFrom } from "@angular/core"; +import * as Flow from "@flowjs/flow.js"; +import {provideRouter} from "@angular/router"; -platformBrowserDynamic().bootstrapModule(AppModule) +bootstrapApplication(AppComponent, { + providers: [ + provideRouter(appRoutes), + importProvidersFrom(NgbModule, BrowserModule, translationModule, NgSelectModule, FormsModule, NgIdleKeepaliveModule.forRoot(), MarkdownModule.forRoot({ + markedOptions: { + provide: MARKED_OPTIONS, + useValue: { + breaks: true + } + } + }), NgxFlowModule, NgOptimizedImage), + ReceiptValidatorDirective, + { provide: 'MockEngine', useValue: mockEngine }, + TranslatorPipe, TranslateService, + { provide: HTTP_INTERCEPTORS, useClass: appInterceptor, multi: true }, + { provide: APP_BASE_HREF, useValue: "/" }, + { provide: LocationStrategy, useClass: HashLocationStrategy }, + { provide: HTTP_INTERCEPTORS, useClass: ErrorCatchingInterceptor, multi: true }, + { provide: HTTP_INTERCEPTORS, useClass: CompletedInterceptor, multi: true }, + { provide: FlowInjectionToken, useValue: Flow }, + { provide: NgbDatepickerI18n, useClass: CustomDatepickerI18n }, + provideHttpClient(withInterceptorsFromDi()), + provideAnimations(), + ] +}) .catch(err => console.error(err)); diff --git a/client/app/src/app-guard.service.ts b/client/app/src/app-guard.service.ts index 7ccd5bc939..5babd19948 100644 --- a/client/app/src/app-guard.service.ts +++ b/client/app/src/app-guard.service.ts @@ -1,20 +1,20 @@ -import {Injectable} from "@angular/core"; +import { Injectable, inject } from "@angular/core"; import {Router, UrlTree} from "@angular/router"; import {Observable} from "rxjs"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {AppDataService} from "@app/app-data.service"; -import {UtilsService} from "@app/shared/services/utils.service"; @Injectable({ providedIn: "root" }) export class SessionGuard { - constructor(private router: Router, private appDataService: AppDataService, public authenticationService: AuthenticationService, protected utilsService: UtilsService) { - } + private router = inject(Router); + private appDataService = inject(AppDataService); + authenticationService = inject(AuthenticationService); canActivate(): Observable | Promise | boolean | UrlTree { if (!this.authenticationService.session) { - this.utilsService.routeGuardRedirect(); + this.router.navigateByUrl("/login").then(); return false; } else { this.appDataService.page = this.router.url; diff --git a/client/app/src/app.module.ts b/client/app/src/app.module.ts deleted file mode 100644 index 9594066347..0000000000 --- a/client/app/src/app.module.ts +++ /dev/null @@ -1,196 +0,0 @@ -import {HostListener, NgModule, CUSTOM_ELEMENTS_SCHEMA, OnDestroy} from "@angular/core"; -import {BrowserModule} from "@angular/platform-browser"; -import {AppRoutingModule} from "@app/app-routing.module"; -import {AppComponent} from "@app/pages/app/app.component"; -import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule} from "@angular/common/http"; -import {AuthModule} from "@app/pages/auth/auth.module"; -import {APP_BASE_HREF, HashLocationStrategy, LocationStrategy, NgOptimizedImage,} from "@angular/common"; -import {SharedModule} from "@app/shared.module"; -import {HeaderComponent} from "@app/shared/partials/header/header.component"; -import {UserComponent} from "@app/shared/partials/header/template/user/user.component"; -import {TranslateLoader, TranslateModule, TranslateService} from "@ngx-translate/core"; -import {TranslateHttpLoader} from "@ngx-translate/http-loader"; -import { - CompletedInterceptor, - ErrorCatchingInterceptor, - appInterceptor -} from "@app/services/root/app-interceptor.service"; -import {Keepalive, NgIdleKeepaliveModule} from "@ng-idle/keepalive"; -import {DEFAULT_INTERRUPTSOURCES, Idle} from "@ng-idle/core"; -import {AuthenticationService} from "@app/services/helper/authentication.service"; -import {HomeComponent} from "@app/pages/dashboard/home/home.component"; -import {TranslatorPipe} from "@app/shared/pipes/translate"; -import {NgSelectModule} from "@ng-select/ng-select"; -import {FormsModule} from "@angular/forms"; -import {ActionModule} from "@app/pages/action/action.module"; -import {WhistleblowerModule} from "@app/pages/whistleblower/whistleblower.module"; -import {MarkdownModule, MarkedOptions, MARKED_OPTIONS} from "ngx-markdown"; -import {ReceiptValidatorDirective} from "@app/shared/directive/receipt-validator.directive"; -import {NgxFlowModule, FlowInjectionToken} from "@flowjs/ngx-flow"; -import * as Flow from "@flowjs/flow.js"; -import {NgbModule, NgbPaginationConfig} from '@ng-bootstrap/ng-bootstrap'; -import {SignupModule} from "@app/pages/signup/signup.module"; -import {WizardModule} from "@app/pages/wizard/wizard.module"; -import {RecipientModule} from "@app/pages/recipient/recipient.module"; -import {AdminModule} from "@app/pages/admin/admin.module"; -import {CustodianModule} from "@app/pages/custodian/custodian.module"; -import {BrowserAnimationsModule} from "@angular/platform-browser/animations"; -import {AnalystModule} from "@app/pages/analyst/analyst.module"; -import {mockEngine} from './services/helper/mocks'; -import {HttpService} from "./shared/services/http.service"; -import {CryptoService} from "@app/shared/services/crypto.service"; -import {TranslationService} from "@app/services/helper/translation.service"; -import {NgbDatepickerI18n} from '@ng-bootstrap/ng-bootstrap'; -import {CustomDatepickerI18n} from '@app/shared/services/custom-datepicker-i18n'; -import {registerLocales} from '@app/services/helper/locale-provider'; -import {ResourceLoaderService} from '@app/services/helper/resource-loader.service'; - -// Register all the locales -registerLocales(); - -export function createTranslateLoader(http: HttpClient) { - return new TranslateHttpLoader(http, "l10n/", ""); -} - -(window as any).mockEngine = mockEngine; -declare global { - interface Window { - GL: { - language: string; - mockEngine: any; - }; - } -} -window.GL = { - language: 'en', // Assuming a default language - mockEngine: mockEngine -}; - -const translationModule = TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useFactory: createTranslateLoader, - deps: [HttpClient], - }, - }) -; - -@NgModule({ - declarations: [AppComponent, HomeComponent, HeaderComponent, UserComponent], - imports: [ - AppRoutingModule, - NgbModule, - HttpClientModule, - BrowserModule, - BrowserAnimationsModule, - AuthModule, - SignupModule, - ActionModule, - WizardModule, - AdminModule, - RecipientModule, - translationModule, - NgSelectModule, - FormsModule, - WhistleblowerModule, - CustodianModule, - AnalystModule, - SharedModule, - NgIdleKeepaliveModule.forRoot(), - MarkdownModule.forRoot({ - markedOptions: { - provide: MARKED_OPTIONS, - useValue: { - breaks: true - } - } - }), - NgxFlowModule, - NgOptimizedImage - ], - providers: [ - ReceiptValidatorDirective, - {provide: 'MockEngine', useValue: mockEngine}, - TranslatorPipe, TranslateService, - {provide: HTTP_INTERCEPTORS, useClass: appInterceptor, multi: true}, - {provide: APP_BASE_HREF, useValue: "/"}, - {provide: LocationStrategy, useClass: HashLocationStrategy}, - {provide: HTTP_INTERCEPTORS, useClass: ErrorCatchingInterceptor, multi: true}, - {provide: HTTP_INTERCEPTORS, useClass: CompletedInterceptor, multi: true}, - {provide: FlowInjectionToken, useValue: Flow}, - {provide: LocationStrategy, useClass: HashLocationStrategy}, - { - provide: NgbPaginationConfig, - useFactory: () => { - const config = new NgbPaginationConfig(); - config.size = 'sm'; // Set pagination size (sm for small, lg for large) - config.boundaryLinks = true; // Display boundary links (first/last) - config.directionLinks = true; // Display previous/next buttons - config.maxSize = 20; // Maximum number of pages displayed - config.rotate = true; // Whether to rotate pages when maxSize > number of pages. - config.ellipses = true; // If true, the ellipsis symbols and first/last page numbers - // will be shown when maxSize > number of pages. - return config; - } - }, - {provide: NgbDatepickerI18n, useClass: CustomDatepickerI18n} - ], - bootstrap: [AppComponent], - schemas: [CUSTOM_ELEMENTS_SCHEMA], -}) -export class AppModule implements OnDestroy { - - constructor(private cryptoService:CryptoService, private authenticationService: AuthenticationService, private idle: Idle, private keepalive: Keepalive, private httpService: HttpService, private resourceLoader: ResourceLoaderService) { - this.initIdleState(); - this.loadNonCriticalResources(); - } - - @HostListener("window:beforeunload") - async ngOnDestroy() { - this.reset(); - } - - initIdleState() { - this.idle.setIdle(1500); - this.idle.setTimeout(300); - this.keepalive.interval(30); - this.idle.setInterrupts(DEFAULT_INTERRUPTSOURCES); - - this.keepalive.onPing.subscribe(() => { - if (this.authenticationService && this.authenticationService.session) { - const token = this.authenticationService.session.token; - this.cryptoService.proofOfWork(token.id).subscribe((result) => { - const param = {'token': token.id + ":" + result}; - this.httpService.requestRefreshUserSession(param).subscribe((result => { - this.authenticationService.session.token = result.token; - })); - }); - } - }); - - this.idle.onTimeout.subscribe(() => { - if (this.authenticationService && this.authenticationService.session) { - if (this.authenticationService.session.role === "whistleblower") { - window.location.replace("about:blank"); - } else { - this.authenticationService.deleteSession(); - this.authenticationService.loginRedirect(); - } - } - }); - - this.reset(); - } - - // Method to load non-critical resources dynamically - loadNonCriticalResources() { - // Load CSS file dynamically - this.resourceLoader.loadStyle('/css/fonts.css'); - } - - reset() { - this.idle.watch(); - this.authenticationService.reset(); - } -} - diff --git a/client/app/src/app-routing.module.ts b/client/app/src/app.routes.ts similarity index 58% rename from client/app/src/app-routing.module.ts rename to client/app/src/app.routes.ts index 7068b44522..94a959e428 100644 --- a/client/app/src/app-routing.module.ts +++ b/client/app/src/app.routes.ts @@ -1,39 +1,28 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; +import {Routes} from "@angular/router"; import {SessionGuard} from "@app/app-guard.service"; -import {HomeComponent} from "@app/pages/dashboard/home/home.component"; -import { - PasswordResetResponseComponent -} from "@app/pages/auth/password-reset-response/password-reset-response.component"; import {AdminGuard} from "@app/shared/guards/admin.guard"; import {CustodianGuard} from "@app/shared/guards/custodian.guard"; import {ReceiverGuard} from "@app/shared/guards/receiver.guard"; import {AnalystGuard} from "@app/shared/guards/analyst.guard"; import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; import {Pageguard} from "@app/shared/guards/pageguard.service"; -import {ActivationComponent} from "@app/pages/signup/templates/activation/activation.component"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {RTipsResolver} from "@app/shared/resolvers/r-tips-resolver.service"; -import {TipComponent} from "@app/pages/recipient/tip/tip.component"; import {TitleResolver} from "@app/shared/resolvers/title-resolver.resolver"; import {IarResolver} from "@app/shared/resolvers/iar-resolver.service"; -import {BlankComponent} from "@app/shared/blank/blank.component"; import {WbTipResolver} from "@app/shared/resolvers/wb-tip-resolver.service"; import {WhistleblowerLoginResolver} from "@app/shared/resolvers/whistleblower-login.resolver"; -import {SubmissionComponent} from "@app/pages/whistleblower/submission/submission.component"; -import {AuthRoutingModule} from "@app/pages/auth/auth-routing.module"; - -const routes: Routes = [ +export const appRoutes: Routes = [ { path: "blank", pathMatch: "full", - component: BlankComponent + loadComponent: () => import('@app/shared/blank/blank.component').then(m => m.BlankComponent) }, { path: "", canActivate: [Pageguard], - component: HomeComponent, + loadComponent: () => import('@app/pages/dashboard/home/home.component').then(m => m.HomeComponent), data: {pageTitle: ""}, pathMatch: "full", resolve: { @@ -43,7 +32,7 @@ const routes: Routes = [ { path: "submission", canActivate: [Pageguard], - component: SubmissionComponent, + loadComponent: () => import('@app/pages/whistleblower/submission/submission.component').then(m => m.SubmissionComponent), data: {pageTitle: ""}, pathMatch: "full", resolve: { @@ -54,7 +43,7 @@ const routes: Routes = [ path: "login", canActivate: [Pageguard], data: {pageTitle: "Log in"}, - loadChildren: () => AuthRoutingModule, + loadChildren: () => import("./pages/auth/auth.routes").then(m => m.authRoutes), }, { path: "signup", @@ -62,17 +51,16 @@ const routes: Routes = [ resolve: { PreferenceResolver }, - loadChildren: () => import("./pages/signup/signup-routing.module").then(m => m.SignupRoutingModule) - + loadChildren: () => import("./pages/signup/signup.routes").then(m => m.signupRoutes) }, { path: "action", - loadChildren: () => import("./pages/action/action-routing.module").then(m => m.ActionRoutingModule) + loadChildren: () => import("./pages/action/action.routes").then(m => m.actionRoutes) }, { path: "recipient", canActivate: [ReceiverGuard], - loadChildren: () => import("./pages/recipient/recipient-routing.module").then(m => m.RecipientRoutingModule), + loadChildren: () => import("./pages/recipient/recipient.routes").then(m => m.recipientRoutes), data: { sidebar: "recipient-sidebar" } @@ -83,7 +71,7 @@ const routes: Routes = [ resolve: { PreferenceResolver, NodeResolver, RtipsResolver: RTipsResolver, IarsResolver: IarResolver }, - loadChildren: () => import("./pages/custodian/custodian-routing.module").then(m => m.CustodianRoutingModule), + loadChildren: () => import("./pages/custodian/custodian.routes").then(m => m.custodianRoutes), data: { sidebar: "custodian-sidebar", pageTitle: "Home", @@ -92,7 +80,7 @@ const routes: Routes = [ { path: "analyst", canActivate: [AnalystGuard], - loadChildren: () => import("./pages/analyst/analyst-routing.module").then(m => m.AnalystRoutingModule), + loadChildren: () => import("./pages/analyst/analyst.routes").then(m => m.analystRoutes), data: { sidebar: "analyst-sidebar", pageTitle: "Home", @@ -101,21 +89,21 @@ const routes: Routes = [ { path: "admin", canActivate: [AdminGuard], - loadChildren: () => import("./pages/admin/admin-routing.module").then(m => m.AdminRoutingModule), + loadChildren: () => import("./pages/admin/admin.routes").then(m => m.adminRoutes), data: { sidebar: "admin-sidebar", - pageTitle: "Log in", + pageTitle: "Home", }, }, { path: "password/reset", data: {pageTitle: "Password reset"}, - component: PasswordResetResponseComponent, + loadComponent: () => import('@app/pages/auth/password-reset-response/password-reset-response.component').then(m => m.PasswordResetResponseComponent), }, { path: "activation", data: {pageTitle: "Sign up"}, - component: ActivationComponent, + loadComponent: () => import('@app/pages/signup/templates/activation/activation.component').then(m => m.ActivationComponent), }, { path: "wizard", @@ -124,7 +112,7 @@ const routes: Routes = [ PreferenceResolver, title: TitleResolver }, - loadChildren: () => import("./pages/wizard/wizard-routing.module").then(m => m.WizardRoutingModule) + loadChildren: () => import("./pages/wizard/wizard.routes").then(m => m.wizardRoutes) }, { path: "reports/:tip_id", @@ -132,17 +120,9 @@ const routes: Routes = [ resolve: { PreferenceResolver, }, - component: TipComponent, + loadComponent: () => import('@app/pages/recipient/tip/tip.component').then(m => m.TipComponent), canActivate: [SessionGuard], pathMatch: "full", }, {path: "**", redirectTo: ""} -]; - -@NgModule({ - imports: [RouterModule.forRoot(routes)], - exports: [RouterModule], -}) -export class AppRoutingModule { - -} +]; \ No newline at end of file diff --git a/client/app/src/models/authentication/session.ts b/client/app/src/models/authentication/session.ts index 2a79168c55..7c3da163bf 100644 --- a/client/app/src/models/authentication/session.ts +++ b/client/app/src/models/authentication/session.ts @@ -1,4 +1,7 @@ +import {redirectResolverModel} from "../resolvers/redirect-resolver-model"; + export class Session { + redirect: redirectResolverModel; id: string; role: string; encryption: boolean; @@ -10,7 +13,6 @@ export class Session { two_factor: boolean; permissions: { can_upload_files: boolean }; token: any; - redirect: string; } export interface Properties { diff --git a/client/app/src/pages/action/action-routing.module.ts b/client/app/src/pages/action/action-routing.module.ts deleted file mode 100644 index bf25f54d9a..0000000000 --- a/client/app/src/pages/action/action-routing.module.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {ForcedTwoFactorComponent} from "@app/pages/action/forced-two-factor/forced-two-factor.component"; -import {ForcePasswordChangeComponent} from "@app/pages/action/force-password-change/force-password-change.component"; - -const routes: Routes = [ - { - path: "forcedtwofactor", - component: ForcedTwoFactorComponent, - pathMatch: "full", - data: {pageTitle: "Password reset"}, - }, { - path: "forcedpasswordchange", - component: ForcePasswordChangeComponent, - pathMatch: "full", - data: {pageTitle: "Password reset"}, - } - -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], -}) -export class ActionRoutingModule { -} \ No newline at end of file diff --git a/client/app/src/pages/action/action.module.ts b/client/app/src/pages/action/action.module.ts deleted file mode 100644 index 558b4fe365..0000000000 --- a/client/app/src/pages/action/action.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {ForcedTwoFactorComponent} from "@app/pages/action/forced-two-factor/forced-two-factor.component"; -import {SharedModule} from "@app/shared.module"; -import {TranslateModule} from "@ngx-translate/core"; -import {ForcePasswordChangeComponent} from "@app/pages/action/force-password-change/force-password-change.component"; -import {FormsModule, ReactiveFormsModule} from "@angular/forms"; - - -@NgModule({ - declarations: [ - ForcedTwoFactorComponent, - ForcePasswordChangeComponent - ], - imports: [ - CommonModule, - TranslateModule, - FormsModule, - ReactiveFormsModule, - SharedModule - ] -}) -export class ActionModule { -} \ No newline at end of file diff --git a/client/app/src/pages/action/action.routes.ts b/client/app/src/pages/action/action.routes.ts new file mode 100644 index 0000000000..54e408ad77 --- /dev/null +++ b/client/app/src/pages/action/action.routes.ts @@ -0,0 +1,15 @@ +import {Routes} from "@angular/router"; + +export const actionRoutes: Routes = [ + { + path: "forcedtwofactor", + loadComponent: () => import('@app/pages/action/forced-two-factor/forced-two-factor.component').then(m => m.ForcedTwoFactorComponent), + pathMatch: "full", + data: {pageTitle: "Password reset"}, + }, { + path: "forcedpasswordchange", + loadComponent: () => import('@app/pages/action/force-password-change/force-password-change.component').then(m => m.ForcePasswordChangeComponent), + pathMatch: "full", + data: {pageTitle: "Password reset"}, + } +]; \ No newline at end of file diff --git a/client/app/src/pages/action/force-password-change/force-password-change.component.ts b/client/app/src/pages/action/force-password-change/force-password-change.component.ts index f20cfbf936..7301498b2d 100644 --- a/client/app/src/pages/action/force-password-change/force-password-change.component.ts +++ b/client/app/src/pages/action/force-password-change/force-password-change.component.ts @@ -1,8 +1,13 @@ import {Component} from "@angular/core"; +import { PasswordChangeComponent } from "../../../shared/partials/password-change/password-change.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-force-password-change", - templateUrl: "./force-password-change.component.html" + selector: "src-force-password-change", + templateUrl: "./force-password-change.component.html", + standalone: true, + imports: [PasswordChangeComponent, TranslateModule, TranslatorPipe] }) export class ForcePasswordChangeComponent { diff --git a/client/app/src/pages/action/forced-two-factor/forced-two-factor.component.ts b/client/app/src/pages/action/forced-two-factor/forced-two-factor.component.ts index 9fc0e4ee26..d520f4a8a7 100644 --- a/client/app/src/pages/action/forced-two-factor/forced-two-factor.component.ts +++ b/client/app/src/pages/action/forced-two-factor/forced-two-factor.component.ts @@ -1,16 +1,27 @@ -import {Component} from "@angular/core"; +import { Component, inject } from "@angular/core"; import {TwoFactorAuthData} from "@app/services/helper/2fa.data.service"; import {HttpService} from "@app/shared/services/http.service"; import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {Router} from "@angular/router"; +import { Enable2fa } from "../../../shared/partials/enable-2fa/enable-2fa"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-forced-two-factor", - templateUrl: "./forced-two-factor.component.html" + selector: "src-forced-two-factor", + templateUrl: "./forced-two-factor.component.html", + standalone: true, + imports: [Enable2fa, TranslateModule, TranslatorPipe] }) export class ForcedTwoFactorComponent { - constructor(protected twoFactorAuthData: TwoFactorAuthData, private httpService: HttpService, private preferenceResolver: PreferenceResolver, private authenticationService: AuthenticationService, private router: Router) { + protected twoFactorAuthData = inject(TwoFactorAuthData); + private httpService = inject(HttpService); + private preferenceResolver = inject(PreferenceResolver); + private authenticationService = inject(AuthenticationService); + private router = inject(Router); + + constructor() { this.twoFactorAuthData.totp.secret = "" this.twoFactorAuthData.totp.token = "" } diff --git a/client/app/src/pages/admin/admin-preferences/admin-preferences.component.ts b/client/app/src/pages/admin/admin-preferences/admin-preferences.component.ts index 57e660ef9e..400ad338a3 100644 --- a/client/app/src/pages/admin/admin-preferences/admin-preferences.component.ts +++ b/client/app/src/pages/admin/admin-preferences/admin-preferences.component.ts @@ -1,8 +1,11 @@ import {Component} from "@angular/core"; +import { PreferencesComponent } from "../../../shared/partials/preferences/preferences.component"; @Component({ - selector: "src-admin-preferences", - templateUrl: "./admin-preferences.component.html" + selector: "src-admin-preferences", + templateUrl: "./admin-preferences.component.html", + standalone: true, + imports: [PreferencesComponent] }) export class AdminPreferencesComponent { diff --git a/client/app/src/pages/admin/admin.module.ts b/client/app/src/pages/admin/admin.module.ts deleted file mode 100644 index 30672b0fac..0000000000 --- a/client/app/src/pages/admin/admin.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {NgbNavModule, NgbModule} from "@ng-bootstrap/ng-bootstrap"; -import {SharedModule} from "@app/shared.module"; -import {SidebarComponent} from "@app/pages/admin/sidebar/sidebar.component"; -import {RouterModule} from "@angular/router"; -import {FormsModule} from "@angular/forms"; -import {AdminPreferencesComponent} from "@app/pages/admin/admin-preferences/admin-preferences.component"; -import {adminHomeComponent} from "@app/pages/admin/home/admin-home.component"; - -@NgModule({ - declarations: [ - AdminPreferencesComponent, - adminHomeComponent, - SidebarComponent, - ], - imports: [ - CommonModule, SharedModule, NgbNavModule, NgbModule, RouterModule, FormsModule - ], - exports: [SidebarComponent] -}) -export class AdminModule { -} \ No newline at end of file diff --git a/client/app/src/pages/admin/admin-routing.module.ts b/client/app/src/pages/admin/admin.routes.ts similarity index 68% rename from client/app/src/pages/admin/admin-routing.module.ts rename to client/app/src/pages/admin/admin.routes.ts index 3c972c30ff..318059b6c3 100644 --- a/client/app/src/pages/admin/admin-routing.module.ts +++ b/client/app/src/pages/admin/admin.routes.ts @@ -1,16 +1,4 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {adminHomeComponent} from "@app/pages/admin/home/admin-home.component"; -import {SettingsModule} from "@app/pages/admin/settings/settings.module"; -import {UsersModule} from "@app/pages/admin/users/users.module"; -import {ContextsModule} from "@app/pages/admin/contexts/contexts.module"; -import {CaseManagementModule} from "@app/pages/admin/casemanagement/case-management.module"; -import {AuditLogModule} from "@app/pages/admin/auditlog/audit-log.module"; -import {NotificationsModule} from "@app/pages/admin/notifications/notifications.module"; -import {SitesModule} from "@app/pages/admin/sites/sites.module"; -import {NetworkModule} from "@app/pages/admin/network/network.module"; -import {QuestionnairesModule} from "@app/pages/admin/questionnaires/questionnaires.module"; -import {AdminPreferencesComponent} from "@app/pages/admin/admin-preferences/admin-preferences.component"; +import {Routes} from "@angular/router"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; import {UsersResolver} from "@app/shared/resolvers/users.resolver"; @@ -25,10 +13,10 @@ import {RedirectsResolver} from "@app/shared/resolvers/redirects.resolver"; import {FieldTemplatesResolver} from "@app/shared/resolvers/field-templates-resolver.service"; import {StatusResolver} from "@app/shared/resolvers/statuses.resolver"; -const routes: Routes = [ +export const adminRoutes: Routes = [ { path: "", - component: adminHomeComponent, + loadComponent: () => import('@app/pages/admin/home/admin-home.component').then(m => m.adminHomeComponent), resolve: { NodeResolver, PreferenceResolver, UsersResolver }, @@ -37,7 +25,7 @@ const routes: Routes = [ }, { path: "home", - component: adminHomeComponent, + loadComponent: () => import('@app/pages/admin/home/admin-home.component').then(m => m.adminHomeComponent), resolve: { NodeResolver, PreferenceResolver, UsersResolver }, @@ -46,7 +34,7 @@ const routes: Routes = [ }, { path: "preferences", - component: AdminPreferencesComponent, + loadComponent: () => import('@app/pages/admin/admin-preferences/admin-preferences.component').then(m => m.AdminPreferencesComponent), resolve: { NodeResolver, PreferenceResolver }, @@ -55,7 +43,7 @@ const routes: Routes = [ }, { path: "settings", - loadChildren: () => SettingsModule, + loadComponent: () => import('@app/pages/admin/settings/settings.component').then(m => m.AdminSettingsComponent), resolve: { NodeResolver, PreferenceResolver, UsersResolver, QuestionnairesResolver }, @@ -64,7 +52,7 @@ const routes: Routes = [ }, { path: "sites", - loadChildren: () => SitesModule, + loadComponent: () => import('@app/pages/admin/sites/sites.component').then(m => m.SitesComponent), resolve: { NodeResolver, PreferenceResolver, UsersResolver, JobResolver, TipsResolver, QuestionnairesResolver, StatusResolver }, @@ -73,7 +61,7 @@ const routes: Routes = [ }, { path: "users", - loadChildren: () => UsersModule, + loadComponent: () => import('@app/pages/admin/users/users.component').then(m => m.UsersComponent), resolve: { NodeResolver, PreferenceResolver, UsersResolver }, @@ -82,7 +70,7 @@ const routes: Routes = [ }, { path: "questionnaires", - loadChildren: () => QuestionnairesModule, + loadComponent: () => import('@app/pages/admin/questionnaires/questionnaires.component').then(m => m.QuestionnairesComponent), resolve: { NodeResolver, PreferenceResolver, ContextsResolver, UsersResolver, QuestionnairesResolver, FieldTemplatesResolver }, @@ -91,7 +79,7 @@ const routes: Routes = [ }, { path: "channels", - loadChildren: () => ContextsModule, + loadComponent: () => import('@app/pages/admin/contexts/contexts.component').then(m => m.ContextsComponent), resolve: { NodeResolver, PreferenceResolver, UsersResolver, QuestionnairesResolver, ContextsResolver }, @@ -100,7 +88,7 @@ const routes: Routes = [ }, { path: "casemanagement", - loadChildren: () => CaseManagementModule, + loadComponent: () => import('@app/pages/admin/casemanagement/case-management.component').then(m => m.CaseManagementComponent), resolve: { NodeResolver, PreferenceResolver, StatuseResolver: StatusResolver }, @@ -109,7 +97,7 @@ const routes: Routes = [ }, { path: "auditlog", - loadChildren: () => AuditLogModule, + loadComponent: () => import('@app/pages/admin/auditlog/audit-log.component').then(m => m.AuditLogComponent), resolve: { NodeResolver, PreferenceResolver, UsersResolver, AuditlogResolver: AuditLogResolver, JobResolver, TipsResolver }, @@ -118,7 +106,7 @@ const routes: Routes = [ }, { path: "notifications", - loadChildren: () => NotificationsModule, + loadComponent: () => import('@app/pages/admin/notifications/notifications.component').then(m => m.NotificationsComponent), resolve: { NodeResolver, PreferenceResolver, NotificationsResolver }, @@ -127,18 +115,11 @@ const routes: Routes = [ }, { path: "network", - loadChildren: () => NetworkModule, + loadComponent: () => import('@app/pages/admin/network/network.component').then(m => m.NetworkComponent), resolve: { NodeResolver, PreferenceResolver, UsersResolver, NetworkResolver, RedirectsResolver }, pathMatch: "full", data: {sidebar: "admin-sidebar", pageTitle: "Network"}, } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], -}) -export class AdminRoutingModule { -} +]; \ No newline at end of file diff --git a/client/app/src/pages/admin/auditlog/audit-log.component.html b/client/app/src/pages/admin/auditlog/audit-log.component.html index e62abc3211..db34088d71 100644 --- a/client/app/src/pages/admin/auditlog/audit-log.component.html +++ b/client/app/src/pages/admin/auditlog/audit-log.component.html @@ -1,24 +1,24 @@
- -
- - - - - - - - - - - - + +
+ + + + + + + + + + + +
\ No newline at end of file diff --git a/client/app/src/pages/admin/auditlog/audit-log.component.ts b/client/app/src/pages/admin/auditlog/audit-log.component.ts index 4023d0faec..343cb71b20 100644 --- a/client/app/src/pages/admin/auditlog/audit-log.component.ts +++ b/client/app/src/pages/admin/auditlog/audit-log.component.ts @@ -1,4 +1,4 @@ -import {AfterViewInit, Component, TemplateRef, ViewChild, ChangeDetectorRef} from "@angular/core"; +import { AfterViewInit, Component, TemplateRef, ViewChild, ChangeDetectorRef, inject } from "@angular/core"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {Tab} from "@app/models/component-model/tab"; @@ -6,12 +6,22 @@ import {AuditLogTab1Component} from "@app/pages/admin/auditlog/auditlog-tab1/aud import {AuditLogTab2Component} from "@app/pages/admin/auditlog/auditlog-tab2/audit-log-tab2.component"; import {AuditLogTab3Component} from "@app/pages/admin/auditlog/auditlog-tab3/audit-log-tab3.component"; import {AuditLogTab4Component} from "@app/pages/admin/auditlog/auditlog-tab4/audit-log-tab4.component"; +import { NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgbNavOutlet } from "@ng-bootstrap/ng-bootstrap"; +import { NgTemplateOutlet } from "@angular/common"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-auditlog", - templateUrl: "./audit-log.component.html" + selector: "src-auditlog", + templateUrl: "./audit-log.component.html", + standalone: true, + imports: [NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgTemplateOutlet, NgbNavOutlet, AuditLogTab1Component, AuditLogTab2Component, AuditLogTab3Component, AuditLogTab4Component, TranslatorPipe, TranslateModule] }) export class AuditLogComponent implements AfterViewInit { + private nodeResolver = inject(NodeResolver); + private authenticationService = inject(AuthenticationService); + private cdr = inject(ChangeDetectorRef); + @ViewChild("tab1") tab1!: TemplateRef; @ViewChild("tab2") tab2!: TemplateRef; @ViewChild("tab3") tab3!: TemplateRef; @@ -21,9 +31,6 @@ export class AuditLogComponent implements AfterViewInit { nodeData: NodeResolver; active: string; - constructor(private nodeResolver: NodeResolver, private authenticationService: AuthenticationService, private cdr: ChangeDetectorRef) { - } - ngAfterViewInit(): void { setTimeout(() => { this.active = "Audit Log"; diff --git a/client/app/src/pages/admin/auditlog/audit-log.module.ts b/client/app/src/pages/admin/auditlog/audit-log.module.ts deleted file mode 100644 index 8e86afde44..0000000000 --- a/client/app/src/pages/admin/auditlog/audit-log.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {AuditLogTab1Component} from "@app/pages/admin/auditlog/auditlog-tab1/audit-log-tab1.component"; -import {AuditLogTab2Component} from "@app/pages/admin/auditlog/auditlog-tab2/audit-log-tab2.component"; -import {AuditLogTab3Component} from "@app/pages/admin/auditlog/auditlog-tab3/audit-log-tab3.component"; -import {AuditLogTab4Component} from "@app/pages/admin/auditlog/auditlog-tab4/audit-log-tab4.component"; -import {AuditLogComponent} from "@app/pages/admin/auditlog/audit-log.component"; -import {SharedModule} from "@app/shared.module"; -import {NgbModule, NgbNavModule} from "@ng-bootstrap/ng-bootstrap"; -import {RouterModule} from "@angular/router"; -import {FormsModule} from "@angular/forms"; -import {NgSelectModule} from "@ng-select/ng-select"; -import {AuditLogRoutingModule} from "@app/pages/admin/auditlog/auditlog-routing.module"; -import {TranslateModule} from "@ngx-translate/core"; - -@NgModule({ - declarations: [ - AuditLogTab1Component, - AuditLogTab2Component, - AuditLogTab3Component, - AuditLogTab4Component, - AuditLogComponent - ], - imports: [ - CommonModule, - AuditLogRoutingModule, SharedModule, NgbNavModule, NgbModule, RouterModule, FormsModule, NgSelectModule, TranslateModule - ] -}) -export class AuditLogModule { -} diff --git a/client/app/src/pages/admin/auditlog/auditlog-routing.module.ts b/client/app/src/pages/admin/auditlog/auditlog-routing.module.ts deleted file mode 100644 index c0810998a1..0000000000 --- a/client/app/src/pages/admin/auditlog/auditlog-routing.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {AuditLogComponent} from "@app/pages/admin/auditlog/audit-log.component"; - -const routes: Routes = [ - { - path: "", - component: AuditLogComponent, - pathMatch: "full", - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class AuditLogRoutingModule { -} diff --git a/client/app/src/pages/admin/auditlog/auditlog-tab1/audit-log-tab1.component.html b/client/app/src/pages/admin/auditlog/auditlog-tab1/audit-log-tab1.component.html index 559a0548f5..c144ef7f0f 100644 --- a/client/app/src/pages/admin/auditlog/auditlog-tab1/audit-log-tab1.component.html +++ b/client/app/src/pages/admin/auditlog/auditlog-tab1/audit-log-tab1.component.html @@ -1,28 +1,36 @@ - - - +@if (this.auditLog.slice()) { +
+ + + + + + + + + + @for (data of getPaginatedData(); track data) { - - - - + + + + - - - - - - - - - -
{{'Date'|translate}}{{'Type'|translate}}{{'User'|translate}}{{'Object'|translate}}
{{'Date'|translate}}{{'Type'|translate}}{{'User'|translate}}{{'Object'|translate}}{{ data.date | date:'dd-MM-yyyy HH:mm' }}{{ data.type }}{{ data.user_id }}{{ data.object_id }}
{{ data.date | date:'dd-MM-yyyy HH:mm' }}{{ data.type }}{{ data.user_id }}{{ data.object_id }}
-
- - - - - - -
globaleaks.log access.log
-
+ } + + + @if (auditLog.length > pageSize) { +
+ + + < {{'Previous' | translate}} + {{'Next' | translate}} > + + << {{'First' | translate}} + {{'Last' | translate}} >> + +
+ }
@if (nodeResolver.dataModel.root_tenant) { + globaleaks.log access.log + }
+} diff --git a/client/app/src/pages/admin/auditlog/auditlog-tab1/audit-log-tab1.component.ts b/client/app/src/pages/admin/auditlog/auditlog-tab1/audit-log-tab1.component.ts index b0568d825c..04d62f91a1 100644 --- a/client/app/src/pages/admin/auditlog/auditlog-tab1/audit-log-tab1.component.ts +++ b/client/app/src/pages/admin/auditlog/auditlog-tab1/audit-log-tab1.component.ts @@ -1,22 +1,30 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {auditlogResolverModel} from "@app/models/resolvers/auditlog-resolver-model"; import {AuditLogResolver} from "@app/shared/resolvers/audit-log-resolver.service"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {UtilsService} from "@app/shared/services/utils.service"; import {AuthenticationService} from "@app/services/helper/authentication.service"; +import { DatePipe } from "@angular/common"; +import { NgbPagination, NgbPaginationPrevious, NgbPaginationNext, NgbPaginationFirst, NgbPaginationLast } from "@ng-bootstrap/ng-bootstrap"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-auditlog-tab1", - templateUrl: "./audit-log-tab1.component.html" + selector: "src-auditlog-tab1", + templateUrl: "./audit-log-tab1.component.html", + standalone: true, + imports: [NgbPagination, NgbPaginationPrevious, NgbPaginationNext, NgbPaginationFirst, NgbPaginationLast, DatePipe, TranslatorPipe, TranslateModule] }) export class AuditLogTab1Component implements OnInit { + protected authenticationService = inject(AuthenticationService); + private auditLogResolver = inject(AuditLogResolver); + protected nodeResolver = inject(NodeResolver); + protected utilsService = inject(UtilsService); + currentPage = 1; pageSize = 20; auditLog: auditlogResolverModel[] = []; - constructor(protected authenticationService: AuthenticationService, private auditLogResolver: AuditLogResolver, protected nodeResolver: NodeResolver, protected utilsService: UtilsService) { - } - ngOnInit() { this.loadAuditLogData(); } diff --git a/client/app/src/pages/admin/auditlog/auditlog-tab2/audit-log-tab2.component.html b/client/app/src/pages/admin/auditlog/auditlog-tab2/audit-log-tab2.component.html index 86291026f8..2f8f758c84 100644 --- a/client/app/src/pages/admin/auditlog/auditlog-tab2/audit-log-tab2.component.html +++ b/client/app/src/pages/admin/auditlog/auditlog-tab2/audit-log-tab2.component.html @@ -1,34 +1,40 @@ - - - +@if (this.users.slice()) { +
+ + + + + + + + + + + + + @for (data of getPaginatedData(); track data) { - - - - - - - + + + + + + + - - - - - - - - - - - - -
{{'ID'|translate}}{{'Username'|translate}}{{'Role'|translate}}{{'Name'|translate}}2FA{{'Creation date'|translate}}{{'Last access'|translate}}
{{'ID'|translate}}{{'Username'|translate}}{{'Role'|translate}}{{'Name'|translate}}2FA{{'Creation date'|translate}}{{'Last access'|translate}}{{data.id}}{{data.username}}{{data.role}}{{data.name}} {{data.creation_date | date:'dd-MM-yyyy HH:mm'}}{{data.last_login | date:'dd-MM-yyyy HH:mm'}}
{{data.id}}{{data.username}}{{data.role}}{{data.name}} {{data.creation_date | date:'dd-MM-yyyy HH:mm'}}{{data.last_login | date:'dd-MM-yyyy HH:mm'}}
-
- - - - - - -
-
+ } + + + @if (users.length > pageSize) { +
+ + + < {{'Previous' | translate}} + {{'Next' | translate}} > + + << {{'First' | translate}} + {{'Last' | translate}} >> + +
+ }
+ } diff --git a/client/app/src/pages/admin/auditlog/auditlog-tab2/audit-log-tab2.component.ts b/client/app/src/pages/admin/auditlog/auditlog-tab2/audit-log-tab2.component.ts index 3d019982f3..e6ecca654b 100644 --- a/client/app/src/pages/admin/auditlog/auditlog-tab2/audit-log-tab2.component.ts +++ b/client/app/src/pages/admin/auditlog/auditlog-tab2/audit-log-tab2.component.ts @@ -1,20 +1,26 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {UsersResolver} from "@app/shared/resolvers/users.resolver"; import {userResolverModel} from "@app/models/resolvers/user-resolver-model"; import {UtilsService} from "@app/shared/services/utils.service"; +import { NgClass, DatePipe } from "@angular/common"; +import { NgbPagination, NgbPaginationPrevious, NgbPaginationNext, NgbPaginationFirst, NgbPaginationLast } from "@ng-bootstrap/ng-bootstrap"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-auditlog-tab2", - templateUrl: "./audit-log-tab2.component.html" + selector: "src-auditlog-tab2", + templateUrl: "./audit-log-tab2.component.html", + standalone: true, + imports: [NgClass, NgbPagination, NgbPaginationPrevious, NgbPaginationNext, NgbPaginationFirst, NgbPaginationLast, DatePipe, TranslatorPipe, TranslateModule] }) export class AuditLogTab2Component implements OnInit{ + private utilsService = inject(UtilsService); + protected usersResolver = inject(UsersResolver); + currentPage = 1; pageSize = 20; users: userResolverModel[] = []; - constructor(private utilsService: UtilsService, protected usersResolver: UsersResolver) { - } - ngOnInit() { this.loadAuditLogData(); } diff --git a/client/app/src/pages/admin/auditlog/auditlog-tab3/audit-log-tab3.component.html b/client/app/src/pages/admin/auditlog/auditlog-tab3/audit-log-tab3.component.html index 3ddbf47469..9a7d4cf47d 100644 --- a/client/app/src/pages/admin/auditlog/auditlog-tab3/audit-log-tab3.component.html +++ b/client/app/src/pages/admin/auditlog/auditlog-tab3/audit-log-tab3.component.html @@ -1,43 +1,55 @@ - - - +@if (this.tips.slice()) { +
+ + + + + + + + + + + + + + + + + @for (data of getPaginatedData(); track data) { - - - - - - - - - - - + + + + + @if (appDataService.contexts_by_id[data.context_id]) { + + } + @if (!appDataService.contexts_by_id[data.context_id]) { + + } + + + + + + - - - - - - - - - - - - - - - - - -
{{'Date'|translate}}{{'Last update'|translate}}{{'Expiration date'|translate}}{{'Channel'|translate}}{{'Status'|translate}}{{'Tor'|translate}}{{'Comments'|translate}}{{'Files'|translate}}{{'Receivers'|translate}}{{'Whistleblower\'s last access'|translate}}
{{'Date'|translate}}{{'Last update'|translate}}{{'Expiration date'|translate}}{{'Channel'|translate}}{{'Status'|translate}}{{'Tor'|translate}}{{'Comments'|translate}}{{'Files'|translate}}{{'Receivers'|translate}}{{'Whistleblower\'s last access'|translate}}{{data.progressive}}{{data.creation_date | date:'dd-MM-yyyy HH:mm'}}{{data.last_update | date:'dd-MM-yyyy HH:mm'}}{{data.expiration_date | date:'dd-MM-yyyy HH:mm'}}{{appDataService.contexts_by_id[data.context_id]['name']}}{{utilsService.getSubmissionStatusText(data.status,data.substatus,appDataService.submissionStatuses)}}@if (data.tor) { + + }{{data.comments}}{{data.files}}{{data.receiver_count}}{{data.last_access | date:'dd-MM-yyyy HH:mm'}}
{{data.progressive}}{{data.creation_date | date:'dd-MM-yyyy HH:mm'}}{{data.last_update | date:'dd-MM-yyyy HH:mm'}}{{data.expiration_date | date:'dd-MM-yyyy HH:mm'}}{{appDataService.contexts_by_id[data.context_id]['name']}}{{utilsService.getSubmissionStatusText(data.status,data.substatus,appDataService.submissionStatuses)}}{{data.comments}}{{data.files}}{{data.receiver_count}}{{data.last_access | date:'dd-MM-yyyy HH:mm'}}
-
- - - - - - -
-
+ } + + + @if (tips.length > pageSize) { +
+ + + < {{'Previous' | translate}} + {{'Next' | translate}} > + + << {{'First' | translate}} + {{'Last' | translate}} >> + +
+ }
+ } diff --git a/client/app/src/pages/admin/auditlog/auditlog-tab3/audit-log-tab3.component.ts b/client/app/src/pages/admin/auditlog/auditlog-tab3/audit-log-tab3.component.ts index 89a3136bfa..6ca618c82b 100644 --- a/client/app/src/pages/admin/auditlog/auditlog-tab3/audit-log-tab3.component.ts +++ b/client/app/src/pages/admin/auditlog/auditlog-tab3/audit-log-tab3.component.ts @@ -1,21 +1,28 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {UtilsService} from "@app/shared/services/utils.service"; import {TipsResolver} from "@app/shared/resolvers/tips.resolver"; import {tipsResolverModel} from "@app/models/resolvers/tips-resolver-model"; import {AppDataService} from "@app/app-data.service"; +import { DatePipe } from "@angular/common"; +import { NgbPagination, NgbPaginationPrevious, NgbPaginationNext, NgbPaginationFirst, NgbPaginationLast } from "@ng-bootstrap/ng-bootstrap"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-auditlog-tab3", - templateUrl: "./audit-log-tab3.component.html" + selector: "src-auditlog-tab3", + templateUrl: "./audit-log-tab3.component.html", + standalone: true, + imports: [NgbPagination, NgbPaginationPrevious, NgbPaginationNext, NgbPaginationFirst, NgbPaginationLast, DatePipe, TranslatorPipe, TranslateModule] }) export class AuditLogTab3Component implements OnInit { + private tipsResolver = inject(TipsResolver); + protected utilsService = inject(UtilsService); + protected appDataService = inject(AppDataService); + currentPage = 1; pageSize = 20; tips: tipsResolverModel[] = []; - constructor(private tipsResolver: TipsResolver, protected utilsService: UtilsService, protected appDataService: AppDataService) { - } - ngOnInit() { this.loadAuditLogData(); } diff --git a/client/app/src/pages/admin/auditlog/auditlog-tab4/audit-log-tab4.component.html b/client/app/src/pages/admin/auditlog/auditlog-tab4/audit-log-tab4.component.html index 4f16e22f55..4eaf2c0ebb 100644 --- a/client/app/src/pages/admin/auditlog/auditlog-tab4/audit-log-tab4.component.html +++ b/client/app/src/pages/admin/auditlog/auditlog-tab4/audit-log-tab4.component.html @@ -1,23 +1,31 @@ - - - +@if (this.jobs.slice()) { +
+ + + + @for (t of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; track t) { + + } + + + + @for (data of getPaginatedData(); track data) { - - - - - - - - + @for (t of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; track t) { + + } - -
#{{ t }}
#{{ t }}
{{data.name}} -
-
{{ (data.timings[t][1] - data.timings[t][0]) / 1000 }}s
-
{{ data.timings[t][0] | date:'HH:mm:ss' }}
-
{{ data.timings[t][0] | date:'dd-MM-yyyy' }}
+
{{data.name}} + @if (data.timings[t]) { +
+
{{ (data.timings[t][1] - data.timings[t][0]) / 1000 }}s
+
{{ data.timings[t][0] | date:'HH:mm:ss' }}
+
{{ data.timings[t][0] | date:'dd-MM-yyyy' }}
+ }
-
-
+ } + + +
+} diff --git a/client/app/src/pages/admin/auditlog/auditlog-tab4/audit-log-tab4.component.ts b/client/app/src/pages/admin/auditlog/auditlog-tab4/audit-log-tab4.component.ts index e8a5c69020..8b237b13a8 100644 --- a/client/app/src/pages/admin/auditlog/auditlog-tab4/audit-log-tab4.component.ts +++ b/client/app/src/pages/admin/auditlog/auditlog-tab4/audit-log-tab4.component.ts @@ -1,20 +1,25 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {JobResolver} from "@app/shared/resolvers/job.resolver"; import {jobResolverModel} from "@app/models/resolvers/job-resolver-model"; import {UtilsService} from "@app/shared/services/utils.service"; +import { DatePipe } from "@angular/common"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-auditlog-tab4", - templateUrl: "./audit-log-tab4.component.html" + selector: "src-auditlog-tab4", + templateUrl: "./audit-log-tab4.component.html", + standalone: true, + imports: [DatePipe, TranslatorPipe, TranslateModule] }) export class AuditLogTab4Component implements OnInit{ + private utilsService = inject(UtilsService); + private jobResolver = inject(JobResolver); + currentPage = 1; pageSize = 20; jobs: jobResolverModel[] = []; - constructor(private utilsService: UtilsService, private jobResolver: JobResolver) { - } - ngOnInit() { this.loadAuditLogData(); } diff --git a/client/app/src/pages/admin/casemanagement/case-management-routing.module.ts b/client/app/src/pages/admin/casemanagement/case-management-routing.module.ts deleted file mode 100644 index 816436d49d..0000000000 --- a/client/app/src/pages/admin/casemanagement/case-management-routing.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {CaseManagementComponent} from "@app/pages/admin/casemanagement/case-management.component"; - -const routes: Routes = [ - { - path: "", - component: CaseManagementComponent, - pathMatch: "full", - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class CaseManagementRoutingModule { -} diff --git a/client/app/src/pages/admin/casemanagement/case-management.component.html b/client/app/src/pages/admin/casemanagement/case-management.component.html index 8a4390ae03..59c35ad926 100644 --- a/client/app/src/pages/admin/casemanagement/case-management.component.html +++ b/client/app/src/pages/admin/casemanagement/case-management.component.html @@ -1,16 +1,16 @@
- -
- - - + +
+ + +
\ No newline at end of file diff --git a/client/app/src/pages/admin/casemanagement/case-management.component.ts b/client/app/src/pages/admin/casemanagement/case-management.component.ts index 66fdef796e..92997c1f94 100644 --- a/client/app/src/pages/admin/casemanagement/case-management.component.ts +++ b/client/app/src/pages/admin/casemanagement/case-management.component.ts @@ -1,23 +1,27 @@ -import {Component, TemplateRef, ViewChild, AfterViewInit, ChangeDetectorRef} from "@angular/core"; +import { Component, TemplateRef, ViewChild, AfterViewInit, ChangeDetectorRef, inject } from "@angular/core"; import {Tab} from "@app/models/component-model/tab"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; -import { - CaseManagementTab1Component -} from "@app/pages/admin/casemanagement/casemanagement-tab1/case-management-tab1.component"; +import {CaseManagementTab1Component} from "@app/pages/admin/casemanagement/casemanagement-tab1/case-management-tab1.component"; +import { FormsModule } from "@angular/forms"; +import { NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgbNavOutlet } from "@ng-bootstrap/ng-bootstrap"; +import { NgTemplateOutlet } from "@angular/common"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-casemanagement", - templateUrl: "./case-management.component.html" + selector: "src-casemanagement", + templateUrl: "./case-management.component.html", + standalone: true, + imports: [FormsModule, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgTemplateOutlet, NgbNavOutlet, CaseManagementTab1Component, TranslatorPipe] }) export class CaseManagementComponent implements AfterViewInit { + protected node = inject(NodeResolver); + private cdr = inject(ChangeDetectorRef); + @ViewChild("tab1") tab1!: TemplateRef; tabs: Tab[]; nodeData: NodeResolver; active: string; - constructor(protected node: NodeResolver, private cdr: ChangeDetectorRef) { - } - ngAfterViewInit(): void { setTimeout(() => { this.active = "Report statuses"; diff --git a/client/app/src/pages/admin/casemanagement/case-management.module.ts b/client/app/src/pages/admin/casemanagement/case-management.module.ts deleted file mode 100644 index 1f3cd66ff0..0000000000 --- a/client/app/src/pages/admin/casemanagement/case-management.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {CaseManagementComponent} from "@app/pages/admin/casemanagement/case-management.component"; -import {SharedModule} from "@app/shared.module"; -import {NgbModule, NgbNavModule} from "@ng-bootstrap/ng-bootstrap"; -import {RouterModule} from "@angular/router"; -import {FormsModule} from "@angular/forms"; -import {NgSelectModule} from "@ng-select/ng-select"; -import {CaseManagementRoutingModule} from "@app/pages/admin/casemanagement/case-management-routing.module"; -import { - CaseManagementTab1Component -} from "@app/pages/admin/casemanagement/casemanagement-tab1/case-management-tab1.component"; -import {SubStatusManagerComponent} from "@app/pages/admin/casemanagement/substatusmanager/sub-status-manager.component"; -import {SubStatusComponent} from "@app/pages/admin/casemanagement/substatuses/sub-status.component"; - - -@NgModule({ - declarations: [ - CaseManagementComponent, - CaseManagementTab1Component, - SubStatusManagerComponent, - SubStatusComponent, - ], - imports: [ - CommonModule, - CaseManagementRoutingModule, SharedModule, NgbNavModule, NgbModule, RouterModule, FormsModule, NgSelectModule - ] -}) -export class CaseManagementModule { -} \ No newline at end of file diff --git a/client/app/src/pages/admin/casemanagement/casemanagement-tab1/case-management-tab1.component.html b/client/app/src/pages/admin/casemanagement/casemanagement-tab1/case-management-tab1.component.html index a40a9c3940..484d803339 100644 --- a/client/app/src/pages/admin/casemanagement/casemanagement-tab1/case-management-tab1.component.html +++ b/client/app/src/pages/admin/casemanagement/casemanagement-tab1/case-management-tab1.component.html @@ -1,31 +1,35 @@
-
- {{'Report statuses'|translate}} - +
+ @if (showAddStatus) { +
+
+
+ +
+ +
+
+
+ -
-
-
-
- -
- -
-
-
- -
+
+
-
- + } + @for (submissions_status of appDataServices.submissionStatuses; track submissions_status; let index = $index; let first = $first; let last = $last) { +
+
+ }
\ No newline at end of file diff --git a/client/app/src/pages/admin/casemanagement/casemanagement-tab1/case-management-tab1.component.ts b/client/app/src/pages/admin/casemanagement/casemanagement-tab1/case-management-tab1.component.ts index 9b46130d77..ef282cd913 100644 --- a/client/app/src/pages/admin/casemanagement/casemanagement-tab1/case-management-tab1.component.ts +++ b/client/app/src/pages/admin/casemanagement/casemanagement-tab1/case-management-tab1.component.ts @@ -1,19 +1,27 @@ -import {Component} from "@angular/core"; +import { Component, inject } from "@angular/core"; import {AppDataService} from "@app/app-data.service"; import {HttpService} from "@app/shared/services/http.service"; import {UtilsService} from "@app/shared/services/utils.service"; +import { NgClass } from "@angular/common"; +import { FormsModule } from "@angular/forms"; +import { SubStatusManagerComponent } from "../substatusmanager/sub-status-manager.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-casemanagement-tab1", - templateUrl: "./case-management-tab1.component.html" + selector: "src-casemanagement-tab1", + templateUrl: "./case-management-tab1.component.html", + standalone: true, + imports: [FormsModule, NgClass, SubStatusManagerComponent, TranslatorPipe] }) export class CaseManagementTab1Component { + private utilsService = inject(UtilsService); + protected appDataServices = inject(AppDataService); + private appDataService = inject(AppDataService); + private httpService = inject(HttpService); + showAddStatus = false; newSubmissionsStatus: { label: string; } = {label: ""}; - constructor(private utilsService: UtilsService, protected appDataServices: AppDataService, private appDataService: AppDataService, private httpService: HttpService) { - } - toggleAddStatus() { this.showAddStatus = !this.showAddStatus; }; diff --git a/client/app/src/pages/admin/casemanagement/substatuses/sub-status.component.html b/client/app/src/pages/admin/casemanagement/substatuses/sub-status.component.html index 197b455b5e..4cb8cf9d65 100644 --- a/client/app/src/pages/admin/casemanagement/substatuses/sub-status.component.html +++ b/client/app/src/pages/admin/casemanagement/substatuses/sub-status.component.html @@ -1,97 +1,117 @@
-
- - {{'Substatuses'|translate}} - - -
-
-
-
-
-
-
- - -
- {{'This field is mandatory'|translate}} -
-
-
-
-
-
-
- -
-
-
+
+ + {{'Substatuses'|translate}} + + +
+ @if (showAddSubStatus) { +
+
+
+
+
+
+ + + @if (newSubStatusList.form.get('label')?.errors?.['required']) { +
+ {{'This field is mandatory'|translate}} +
+ } +
+
+
+
+
+ +
+
+
+
-
-
-
-
- - {{substatus.label}} - - - - - - - - - - - + } +
+ @for (substatus of submissionsStatus.substatuses; track substatus; let index = $index; let first = $first; let last = $last) { +
+
+
+ + {{substatus.label}} + + + + @if (!first) { + + } + @if (!last) { + + } + @if (!this.subStatusEditing[index]) { + + } + @if (this.subStatusEditing[index]) { + + } + @if (this.subStatusEditing[index]) { + + } + + + +
+ @if (this.subStatusEditing[index]) { +
+
+
+
+
+ + +
+
+
+
+ + +
-
-
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
+
+
+ + @if (isCustomOptionSelected(substatus.tip_timetolive_option)) { + + } +
+
+ }
-
+
+ } +
diff --git a/client/app/src/pages/admin/casemanagement/substatuses/sub-status.component.ts b/client/app/src/pages/admin/casemanagement/substatuses/sub-status.component.ts index fd771f0a08..368dcafdbc 100644 --- a/client/app/src/pages/admin/casemanagement/substatuses/sub-status.component.ts +++ b/client/app/src/pages/admin/casemanagement/substatuses/sub-status.component.ts @@ -1,4 +1,4 @@ -import {Component, Input, OnInit} from "@angular/core"; +import { Component, Input, OnInit, inject } from "@angular/core"; import {UtilsService} from "@app/shared/services/utils.service"; import {HttpClient} from "@angular/common/http"; import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; @@ -7,11 +7,21 @@ import {DeleteConfirmationComponent} from "@app/shared/modals/delete-confirmatio import {Observable} from "rxjs"; import {Status, Substatus} from "@app/models/app/public-model"; +import { FormsModule } from "@angular/forms"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; + @Component({ - selector: "src-substatuses", - templateUrl: "./sub-status.component.html" + selector: "src-substatuses", + templateUrl: "./sub-status.component.html", + standalone: true, + imports: [FormsModule, TranslatorPipe] }) export class SubStatusComponent implements OnInit { + private httpService = inject(HttpService); + protected modalService = inject(NgbModal); + protected utilsService = inject(UtilsService); + private http = inject(HttpClient); + @Input() submissionsStatus: Status; subStatusEditing: boolean[] = []; newSubStatus: { label: string; } = {label: ""}; @@ -21,9 +31,6 @@ export class SubStatusComponent implements OnInit { this.showAddSubStatus = !this.showAddSubStatus; } - constructor(private httpService: HttpService, protected modalService: NgbModal, protected utilsService: UtilsService, private http: HttpClient) { - } - ngOnInit(): void { this.subStatusEditing = new Array(this.submissionsStatus.substatuses.length).fill(false); } diff --git a/client/app/src/pages/admin/casemanagement/substatusmanager/sub-status-manager.component.html b/client/app/src/pages/admin/casemanagement/substatusmanager/sub-status-manager.component.html index b4adf2dba2..eab811eaa6 100644 --- a/client/app/src/pages/admin/casemanagement/substatusmanager/sub-status-manager.component.html +++ b/client/app/src/pages/admin/casemanagement/substatusmanager/sub-status-manager.component.html @@ -1,46 +1,64 @@
-
-
-
- {{ submissionsStatus.label ? (submissionsStatus.label | translate) : '' }} - - - - - - - - - - -
-
-
-
-
-
- - -
{{'This field is mandatory'|translate}}
-
-
-
- -
+
+
+
+ {{ submissionsStatus.label ? (submissionsStatus.label | translate) : '' }} + + + @if (!first) { + + } + @if (!last) { + + } + @if (isEditable(submissionsStatus) && !editing) { + + } + @if (editing) { + + } + @if (editing) { + + } + @if (!isSystemDefined(submissionsStatus)) { + + } + + +
+ @if (editing) { +
+
+
+ @if (!isSystemDefined(submissionsStatus)) { +
+
+ + + @if (editsubmissionStatus?.errors?.['required']) { +
{{'This field is mandatory'|translate}}
+ }
+
+ } +
+
+
+ }
+
\ No newline at end of file diff --git a/client/app/src/pages/admin/casemanagement/substatusmanager/sub-status-manager.component.ts b/client/app/src/pages/admin/casemanagement/substatusmanager/sub-status-manager.component.ts index 8dd3f65c34..a21178a94e 100644 --- a/client/app/src/pages/admin/casemanagement/substatusmanager/sub-status-manager.component.ts +++ b/client/app/src/pages/admin/casemanagement/substatusmanager/sub-status-manager.component.ts @@ -1,4 +1,4 @@ -import {Component, Input} from "@angular/core"; +import { Component, Input, inject } from "@angular/core"; import {AppDataService} from "@app/app-data.service"; import {HttpService} from "@app/shared/services/http.service"; import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; @@ -6,12 +6,23 @@ import {UtilsService} from "@app/shared/services/utils.service"; import {DeleteConfirmationComponent} from "@app/shared/modals/delete-confirmation/delete-confirmation.component"; import {Observable} from "rxjs"; import {Status} from "@app/models/app/public-model"; +import { FormsModule } from "@angular/forms"; + +import { SubStatusComponent } from "../substatuses/sub-status.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-substatusmanager", - templateUrl: "./sub-status-manager.component.html" + selector: "src-substatusmanager", + templateUrl: "./sub-status-manager.component.html", + standalone: true, + imports: [FormsModule, SubStatusComponent, TranslatorPipe] }) export class SubStatusManagerComponent { + private appDataServices = inject(AppDataService); + private httpService = inject(HttpService); + private modalService = inject(NgbModal); + private utilsService = inject(UtilsService); + editing = false; @Input() submissionsStatus: Status; @Input() submissionStatuses: Status[]; @@ -19,10 +30,6 @@ export class SubStatusManagerComponent { @Input() first: boolean; @Input() last: boolean; - constructor(private appDataServices: AppDataService, private httpService: HttpService, private modalService: NgbModal, private utilsService: UtilsService) { - - } - isSystemDefined(state: Status): boolean { return ["new", "opened", "closed"].indexOf(state.id) !== -1; } diff --git a/client/app/src/pages/admin/contexts/context-editor/context-editor.component.html b/client/app/src/pages/admin/contexts/context-editor/context-editor.component.html index c482523456..d6cd658ade 100644 --- a/client/app/src/pages/admin/contexts/context-editor/context-editor.component.html +++ b/client/app/src/pages/admin/contexts/context-editor/context-editor.component.html @@ -1,206 +1,248 @@
-
- - {{ contextResolver.name }} - {{ 'Hidden' | translate }} - - - - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
{{ 'This field is mandatory' | translate }}
-
-
- - -
-
- - +
+ + {{ contextResolver.name }} + @if (contextResolver.hidden) { + {{ 'Hidden' | translate }} + } + + + + @if (index !== 0 && !nodeResolver.dataModel.show_contexts_in_alphabetical_order) { + + } + @if (index !== contextsData.length - 1 && !nodeResolver.dataModel.show_contexts_in_alphabetical_order) { + + } + @if (!editing) { + + } + @if (editing) { + + } + @if (editing) { + + } + + + +
+ @if (editing) { +
+
+
+
+
+
+
+
+ + + @if (editContext.form.get('name')?.errors?.['required']) { +
{{ 'This field is mandatory' | translate }}
+ } +
+
+ + +
+
+ + +
+
+ +
+ @if (showSelect) { +
+ + + {{ item.name }} + + +
-
- -
-
- - - {{ item.name }} - - -
-
-
-
    -
  1. - - - - - - - - - - - - - {{adminReceiversById[receiver].name}} -
  2. -
-
-
-
-
-
- {{ 'Reminder date' | translate }} ({{ 'days' | translate }}) -
-
-
- -
-
-
{{ 'Set the value to 0 to disable this feature.' | translate }}
-
-
- -
- -
-
{{ 'Set the value to 0 to disable this feature.' | translate }}
-
-
- - + } +
+
    + @for (receiver of contextResolver.receivers; track receiver; let index = $index) { +
  1. + @if (!contextResolver.show_receivers_in_alphabetical_order) { + + @if (index !== 0) { + + + + } + @if ((index !== contextResolver.receivers.length - 1) || (index !== 0)) { + + } + @if (index !== contextResolver.receivers.length - 1) { + + + + } + + } + + + + {{adminReceiversById[receiver].name}} +
  2. + } +
+
+
+
+
+
+ {{ 'Reminder date' | translate }} ({{ 'days' | translate }})
+
+
+ +
+
+
{{ 'Set the value to 0 to disable this feature.' | translate }}
+
+
+ +
+ +
+
{{ 'Set the value to 0 to disable this feature.' | translate }}
+
+
+ +
+

-
- -
-
-
-
- -
-
- -
-
-
- -
-
- -
{{ 'Set the value to 0 to disable this feature.' | translate }}
-
-
- -
-
-
- - -
-
- -
- - - - - - - - - - - - - - - - - -
{{ 'Threshold' | translate }}{{ 'Value' | translate }}
{{ 'Medium' | translate }} - -
{{ 'High' | translate }} - -
-
-
+
+ +
+ @if (showAdvancedSettings) { +
+
+
+ +
+
+ +
+ @if (contextResolver.allow_recipients_selection) { +
+
+ +
+
+ +
{{ 'Set the value to 0 to disable this feature.' | translate }}
+
+ +
+
+ } +
+ +
+ @if (nodeData.enable_scoring_system) { +
+ +
+ + + + + + + + + + + + + + + + + +
{{ 'Threshold' | translate }}{{ 'Value' | translate }}
{{ 'Medium' | translate }} + +
{{ 'High' | translate }} + +
+
+
+ } +
+ }
+

+}
\ No newline at end of file diff --git a/client/app/src/pages/admin/contexts/context-editor/context-editor.component.ts b/client/app/src/pages/admin/contexts/context-editor/context-editor.component.ts index e6f604efd6..7e0b3cb72f 100644 --- a/client/app/src/pages/admin/contexts/context-editor/context-editor.component.ts +++ b/client/app/src/pages/admin/contexts/context-editor/context-editor.component.ts @@ -1,6 +1,6 @@ import {HttpClient} from "@angular/common/http"; -import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core"; -import {NgForm} from "@angular/forms"; +import { Component, EventEmitter, Input, OnInit, Output, inject } from "@angular/core"; +import { NgForm, FormsModule } from "@angular/forms"; import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; import {DeleteConfirmationComponent} from "@app/shared/modals/delete-confirmation/delete-confirmation.component"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; @@ -12,12 +12,26 @@ import {contextResolverModel} from "@app/models/resolvers/context-resolver-model import {questionnaireResolverModel} from "@app/models/resolvers/questionnaire-model"; import {userResolverModel} from "@app/models/resolvers/user-resolver-model"; import {nodeResolverModel} from "@app/models/resolvers/node-resolver-model"; +import { NgClass } from "@angular/common"; +import { ImageUploadDirective } from "../../../../shared/directive/image-upload.directive"; +import { NgSelectComponent, NgOptionTemplateDirective } from "@ng-select/ng-select"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { FilterPipe } from "@app/shared/pipes/filter.pipe"; @Component({ - selector: "src-context-editor", - templateUrl: "./context-editor.component.html" + selector: "src-context-editor", + templateUrl: "./context-editor.component.html", + standalone: true, + imports: [ImageUploadDirective, FormsModule, NgSelectComponent, NgOptionTemplateDirective, NgClass, TranslatorPipe, FilterPipe] }) export class ContextEditorComponent implements OnInit { + private http = inject(HttpClient); + private modalService = inject(NgbModal); + protected nodeResolver = inject(NodeResolver); + private usersResolver = inject(UsersResolver); + private questionnairesResolver = inject(QuestionnairesResolver); + private utilsService = inject(UtilsService); + @Input() contextsData: contextResolverModel[]; @Input() contextResolver: contextResolverModel; @Input() index: number; @@ -32,9 +46,6 @@ export class ContextEditorComponent implements OnInit { selected = {value: []}; adminReceiversById: { [userId: string]: userResolverModel } = {}; - constructor(private http: HttpClient, private modalService: NgbModal, protected nodeResolver: NodeResolver, private usersResolver: UsersResolver, private questionnairesResolver: QuestionnairesResolver, private utilsService: UtilsService) { - } - ngOnInit(): void { this.questionnairesData = this.questionnairesResolver.dataModel; diff --git a/client/app/src/pages/admin/contexts/contexts-routing.module.ts b/client/app/src/pages/admin/contexts/contexts-routing.module.ts deleted file mode 100644 index 3ae643a4b7..0000000000 --- a/client/app/src/pages/admin/contexts/contexts-routing.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {ContextsComponent} from "@app/pages/admin/contexts/contexts.component"; - -const routes: Routes = [ - { - path: "", - component: ContextsComponent, - pathMatch: "full", - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class ContextsRoutingModule { -} \ No newline at end of file diff --git a/client/app/src/pages/admin/contexts/contexts.component.html b/client/app/src/pages/admin/contexts/contexts.component.html index a6488b74cf..121f918194 100644 --- a/client/app/src/pages/admin/contexts/contexts.component.html +++ b/client/app/src/pages/admin/contexts/contexts.component.html @@ -1,37 +1,39 @@
-
- {{ 'Channels' | translate }} - +
+ @if (showAddContext) { +
+
+
+ +
+ +
+
+
+ -
-
-
-
- -
- -
-
-
- -
+
+
- - -
-
- -
-
-
-
+ } + @if (contextsData.length > 0) { + @for (context of contextsData; track context; let index = $index) { +
+
+ +
+
+ } + }
\ No newline at end of file diff --git a/client/app/src/pages/admin/contexts/contexts.component.ts b/client/app/src/pages/admin/contexts/contexts.component.ts index 1c701f4a54..f9d3ba2955 100644 --- a/client/app/src/pages/admin/contexts/contexts.component.ts +++ b/client/app/src/pages/admin/contexts/contexts.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {NewContext} from "@app/models/admin/new-context"; import {contextResolverModel} from "@app/models/resolvers/context-resolver-model"; import {AuthenticationService} from "@app/services/helper/authentication.service"; @@ -8,20 +8,30 @@ import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; import {UsersResolver} from "@app/shared/resolvers/users.resolver"; import {HttpService} from "@app/shared/services/http.service"; import {UtilsService} from "@app/shared/services/utils.service"; +import { NgClass } from "@angular/common"; +import { FormsModule } from "@angular/forms"; +import { ContextEditorComponent } from "@app/pages/admin/contexts/context-editor/context-editor.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-contexts", - templateUrl: "./contexts.component.html" + selector: "src-contexts", + templateUrl: "./contexts.component.html", + standalone: true, + imports: [FormsModule, NgClass, ContextEditorComponent, TranslatorPipe] }) export class ContextsComponent implements OnInit { + protected preference = inject(PreferenceResolver); + protected httpService = inject(HttpService); + protected authenticationService = inject(AuthenticationService); + protected node = inject(NodeResolver); + protected users = inject(UsersResolver); + protected contexts = inject(ContextsResolver); + protected utilsService = inject(UtilsService); + showAddContext: boolean = false; new_context: { name: string; } = {name: ""}; contextsData: contextResolverModel[] = []; - - constructor(protected preference: PreferenceResolver, protected httpService: HttpService, protected authenticationService: AuthenticationService, protected node: NodeResolver, protected users: UsersResolver, protected contexts: ContextsResolver, protected utilsService: UtilsService) { - } - toggleAddContext() { this.showAddContext = !this.showAddContext; }; diff --git a/client/app/src/pages/admin/contexts/contexts.module.ts b/client/app/src/pages/admin/contexts/contexts.module.ts deleted file mode 100644 index ade75ae1e0..0000000000 --- a/client/app/src/pages/admin/contexts/contexts.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {ContextsRoutingModule} from "@app/pages/admin/contexts/contexts-routing.module"; -import {ContextsComponent} from "@app/pages/admin/contexts/contexts.component"; -import {FormsModule} from "@angular/forms"; -import {RouterModule} from "@angular/router"; -import {NgbNavModule, NgbModule} from "@ng-bootstrap/ng-bootstrap"; -import {NgSelectModule} from "@ng-select/ng-select"; -import {SharedModule} from "@app/shared.module"; -import {ContextEditorComponent} from "@app/pages/admin/contexts/context-editor/context-editor.component"; - - -@NgModule({ - declarations: [ - ContextsComponent, - ContextEditorComponent - ], - imports: [ - CommonModule, - ContextsRoutingModule, SharedModule, NgbNavModule, NgbModule, RouterModule, FormsModule, NgSelectModule - ] -}) -export class ContextsModule { -} diff --git a/client/app/src/pages/admin/home/admin-home.component.html b/client/app/src/pages/admin/home/admin-home.component.html index 2da05413e1..8ed0602735 100644 --- a/client/app/src/pages/admin/home/admin-home.component.html +++ b/client/app/src/pages/admin/home/admin-home.component.html @@ -1,35 +1,37 @@
- -
+ +
\ No newline at end of file diff --git a/client/app/src/pages/admin/home/admin-home.component.ts b/client/app/src/pages/admin/home/admin-home.component.ts index c13c21a82e..a621b617a0 100644 --- a/client/app/src/pages/admin/home/admin-home.component.ts +++ b/client/app/src/pages/admin/home/admin-home.component.ts @@ -1,22 +1,29 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; import {nodeResolverModel} from "@app/models/resolvers/node-resolver-model"; import {preferenceResolverModel} from "@app/models/resolvers/preference-resolver-model"; import { UtilsService } from "@app/shared/services/utils.service"; +import { NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgbNavOutlet } from "@ng-bootstrap/ng-bootstrap"; +import { UserHomeComponent } from "../../../shared/partials/user-home/user-home.component"; + +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-admin-home", - templateUrl: "./admin-home.component.html" + selector: "src-admin-home", + templateUrl: "./admin-home.component.html", + standalone: true, + imports: [NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, UserHomeComponent, NgbNavOutlet, TranslatorPipe] }) export class adminHomeComponent implements OnInit { + private utilsService = inject(UtilsService); + private preference = inject(PreferenceResolver); + protected nodeResolver = inject(NodeResolver); + active: number = 0; nodeData: nodeResolverModel; preferenceData: preferenceResolverModel; - constructor(private utilsService: UtilsService, private preference: PreferenceResolver, protected nodeResolver: NodeResolver) { - } - ngOnInit(): void { if (this.nodeResolver.dataModel) { this.nodeData = this.nodeResolver.dataModel; diff --git a/client/app/src/pages/admin/network/access-control/access-control.component.html b/client/app/src/pages/admin/network/access-control/access-control.component.html index 7e917b0704..857209fb2f 100644 --- a/client/app/src/pages/admin/network/access-control/access-control.component.html +++ b/client/app/src/pages/admin/network/access-control/access-control.component.html @@ -1,76 +1,84 @@
-
-
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{ 'Role' | translate }}{{ 'Enabled' | translate }}{{ 'Allowed IP addresses' | translate }}
{{ 'Admin' | translate }} - - - -
{{ 'Analyst' | translate }} - - - -
{{ 'Custodian' | translate }} - - - -
{{ 'Recipient' | translate }} - - - -
-
-
{{ 'Each entry must be separated with a comma.' | translate }}
-
- {{ 'Example' | translate }}: - 212.35.212.87,192.0.2.0/24,2001:db8::/32 -
-
-
+
+ +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ 'Role' | translate }}{{ 'Enabled' | translate }}{{ 'Allowed IP addresses' | translate }}
{{ 'Admin' | translate }} + + + @if (networkData.ip_filter_admin_enable) { + + } +
{{ 'Analyst' | translate }} + + + @if (networkData.ip_filter_analyst_enable) { + + } +
{{ 'Custodian' | translate }} + + + @if (networkData.ip_filter_custodian_enable) { + + } +
{{ 'Recipient' | translate }} + + + @if (networkData.ip_filter_receiver_enable) { + + } +
+
+
{{ 'Each entry must be separated with a comma.' | translate }}
+
+ {{ 'Example' | translate }}: + 212.35.212.87,192.0.2.0/24,2001:db8::/32
-
-
-
- -
-
- -
+
+
+
+
+
+
+ +
+
+ +
\ No newline at end of file diff --git a/client/app/src/pages/admin/network/access-control/access-control.component.ts b/client/app/src/pages/admin/network/access-control/access-control.component.ts index 696da2feaf..a06d468275 100644 --- a/client/app/src/pages/admin/network/access-control/access-control.component.ts +++ b/client/app/src/pages/admin/network/access-control/access-control.component.ts @@ -1,18 +1,24 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {networkResolverModel} from "@app/models/resolvers/network-resolver-model"; import {NetworkResolver} from "@app/shared/resolvers/network.resolver"; import {HttpService} from "@app/shared/services/http.service"; import {UtilsService} from "@app/shared/services/utils.service"; +import { FormsModule } from "@angular/forms"; + +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-access-control", - templateUrl: "./access-control.component.html" + selector: "src-access-control", + templateUrl: "./access-control.component.html", + standalone: true, + imports: [FormsModule, TranslatorPipe] }) export class AccessControlComponent implements OnInit { - networkData: networkResolverModel; + private networkResolver = inject(NetworkResolver); + private httpService = inject(HttpService); + private utilsService = inject(UtilsService); - constructor(private networkResolver: NetworkResolver, private httpService: HttpService, private utilsService: UtilsService) { - } + networkData: networkResolverModel; ngOnInit(): void { this.networkData = this.networkResolver.dataModel; diff --git a/client/app/src/pages/admin/network/https-csr-gen/https-csr-gen.component.html b/client/app/src/pages/admin/network/https-csr-gen/https-csr-gen.component.html index 4923c54654..d002823a5e 100644 --- a/client/app/src/pages/admin/network/https-csr-gen/https-csr-gen.component.html +++ b/client/app/src/pages/admin/network/https-csr-gen/https-csr-gen.component.html @@ -1,38 +1,40 @@
-
- -
- -
+
+ +
+
-
- -
- -
- {{ 'Invalid email address' | translate }} -
+
+
+ +
+ + @if (certSigRequestForm.form.get('email')?.errors?.['pattern']) { +
+ {{ 'Invalid email address' | translate }}
+ }
-
- -
- -
+
+
+ +
+
-
- -
- -
+
+
+ +
+
-
- -
- -
+
+
+ +
+
- +
+ \ No newline at end of file diff --git a/client/app/src/pages/admin/network/https-csr-gen/https-csr-gen.component.ts b/client/app/src/pages/admin/network/https-csr-gen/https-csr-gen.component.ts index 7252bcb389..2b204e0172 100644 --- a/client/app/src/pages/admin/network/https-csr-gen/https-csr-gen.component.ts +++ b/client/app/src/pages/admin/network/https-csr-gen/https-csr-gen.component.ts @@ -1,15 +1,24 @@ -import {Component, Input} from "@angular/core"; +import { Component, Input, inject } from "@angular/core"; import {FileResources} from "@app/models/component-model/file-resources"; import {Constants} from "@app/shared/constants/constants"; import {HttpService} from "@app/shared/services/http.service"; import {UtilsService} from "@app/shared/services/utils.service"; import {AuthenticationService} from "@app/services/helper/authentication.service"; +import { FormsModule } from "@angular/forms"; +import { NgClass } from "@angular/common"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-https-csr-gen", - templateUrl: "./https-csr-gen.component.html" + selector: "src-https-csr-gen", + templateUrl: "./https-csr-gen.component.html", + standalone: true, + imports: [FormsModule, NgClass, TranslatorPipe] }) export class HttpsCsrGenComponent { + private authenticationService = inject(AuthenticationService); + private httpService = inject(HttpService); + private utilsService = inject(UtilsService); + @Input() fileResources: FileResources; protected readonly Constants = Constants; csr_cfg: { @@ -28,9 +37,6 @@ export class HttpsCsrGenComponent { email: "" }; - constructor(private authenticationService: AuthenticationService, private httpService: HttpService, private utilsService: UtilsService) { - } - submitCSR() { this.httpService.requestCSRDirectContentResource(this.csr_cfg).subscribe({ next: (response) => { diff --git a/client/app/src/pages/admin/network/https-files/https-files.component.html b/client/app/src/pages/admin/network/https-files/https-files.component.html index 15c4e1045f..cd836e28c7 100644 --- a/client/app/src/pages/admin/network/https-files/https-files.component.html +++ b/client/app/src/pages/admin/network/https-files/https-files.component.html @@ -1,114 +1,144 @@
- -
-
- {{ 'Private Key' | translate }} - (PEM) - - - - - - - - - - -
-
-
-
- {{ 'Certificate Signing Request' | translate }} - (PEM) - - - - - -
-
-
-
- + @if (tlsConfig) { +
+
+ {{ 'Private Key' | translate }} + (PEM) + + @if (!tlsConfig.files.key.set) { + + + + + } + @if (!tlsConfig.files.key.set) { + + } + @if (tlsConfig.files.key.set) { + + + + } + +
+
+ @if (tlsConfig.files.key.set && !tlsConfig.acme) { +
+
+ {{ 'Certificate Signing Request' | translate }} + (PEM) + @if (!isCsrSet()) { + + + @if (!csr_state.open) { + + } + @if (csr_state.open) { + + } + +
+
+ @if (csr_state.open) { +
+
+
+ }
- - - - - + } + @if (isCsrSet()) { + + + + + + }
-
+
+ }
-
- {{ 'Certificate' | translate }} - (PEM) - - - {{ 'Valid until:' | translate }} {{ tlsConfig.files.cert.expiration_date | - date:'dd-MM-yyyy HH:mm' }} - {{ 'Issuer:' | translate }} - {{ tlsConfig.files.cert.issuer }} - - - - - - - - - - +
+ {{ 'Certificate' | translate }} + (PEM) + @if (tlsConfig.files.cert.set) { + + {{ 'Valid until:' | translate }} {{ tlsConfig.files.cert.expiration_date | + date:'dd-MM-yyyy HH:mm' }} + {{ 'Issuer:' | translate }} + {{ tlsConfig.files.cert.issuer }} + + } + + @if (!tlsConfig.files.cert.set) { + + + -
+ } + @if (tlsConfig.files.cert.set) { + + + + } +
+
-
- {{ 'Intermediate Certificates' | translate }} - (PEM) - - - {{ 'Valid until:' | translate }} {{ tlsConfig.files.chain.expiration_date | - date:'dd-MM-yyyy HH:mm' }} - {{ 'Issuer:' | translate }} - {{ tlsConfig.files.chain.issuer }} - - -
- - - - - - - -
+
+ {{ 'Intermediate Certificates' | translate }} + (PEM) + @if (tlsConfig.files.chain.set) { + + {{ 'Valid until:' | translate }} {{ tlsConfig.files.chain.expiration_date | + date:'dd-MM-yyyy HH:mm' }} + {{ 'Issuer:' | translate }} + {{ tlsConfig.files.chain.issuer }} + + } +
+ @if (!tlsConfig.files.chain.set) { + + + + + } + @if (tlsConfig.files.chain.set) { + + + + }
+
- - - - -
\ No newline at end of file + } + @if (!tlsConfig.enabled && state > 1) { + + } + @if (tlsConfig.enabled) { + + } + +
\ No newline at end of file diff --git a/client/app/src/pages/admin/network/https-files/https-files.component.ts b/client/app/src/pages/admin/network/https-files/https-files.component.ts index b1a8530a37..2894cd259f 100644 --- a/client/app/src/pages/admin/network/https-files/https-files.component.ts +++ b/client/app/src/pages/admin/network/https-files/https-files.component.ts @@ -1,4 +1,4 @@ -import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from "@angular/core"; +import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild, inject } from "@angular/core"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; import {ConfirmationComponent} from "@app/shared/modals/confirmation/confirmation.component"; @@ -8,12 +8,23 @@ import {UtilsService} from "@app/shared/services/utils.service"; import {nodeResolverModel} from "@app/models/resolvers/node-resolver-model"; import {TlsConfig} from "@app/models/component-model/tls-confiq"; import {FileResource, FileResources} from "@app/models/component-model/file-resources"; +import { DatePipe } from "@angular/common"; +import { HttpsCsrGenComponent } from "../https-csr-gen/https-csr-gen.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-https-files", - templateUrl: "./https-files.component.html" + selector: "src-https-files", + templateUrl: "./https-files.component.html", + standalone: true, + imports: [HttpsCsrGenComponent, DatePipe, TranslatorPipe] }) export class HttpsFilesComponent implements OnInit { + private authenticationService = inject(AuthenticationService); + private nodeResolver = inject(NodeResolver); + private httpService = inject(HttpService); + private modalService = inject(NgbModal); + private utilsService = inject(UtilsService); + @Output() dataToParent = new EventEmitter(); @Input() tlsConfig: TlsConfig; @Input() state: number = 0; @@ -32,9 +43,6 @@ export class HttpsFilesComponent implements OnInit { open: false }; - constructor(private authenticationService: AuthenticationService, private nodeResolver: NodeResolver, private httpService: HttpService, private modalService: NgbModal, private utilsService: UtilsService) { - } - ngOnInit(): void { this.nodeData = this.nodeResolver.dataModel; } @@ -117,4 +125,8 @@ export class HttpsFilesComponent implements OnInit { }; return modalRef.result; } + + isCsrSet(): boolean { + return !!this.tlsConfig.files.csr?.set; + } } \ No newline at end of file diff --git a/client/app/src/pages/admin/network/https-setup/https-setup.component.ts b/client/app/src/pages/admin/network/https-setup/https-setup.component.ts index 0fe4aa1521..38e491dc3f 100644 --- a/client/app/src/pages/admin/network/https-setup/https-setup.component.ts +++ b/client/app/src/pages/admin/network/https-setup/https-setup.component.ts @@ -1,13 +1,19 @@ -import {Component, EventEmitter, Output} from "@angular/core"; +import { Component, EventEmitter, Output, inject } from "@angular/core"; import {FileResources} from "@app/models/component-model/file-resources"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {HttpService} from "@app/shared/services/http.service"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-https-setup", - templateUrl: "./https-setup.component.html" + selector: "src-https-setup", + templateUrl: "./https-setup.component.html", + standalone: true, + imports: [TranslatorPipe] }) export class HttpsSetupComponent { + private httpService = inject(HttpService); + private authenticationService = inject(AuthenticationService); + @Output() dataToParent = new EventEmitter(); fileResources: FileResources = { key: {name: "key"}, @@ -16,9 +22,6 @@ export class HttpsSetupComponent { csr: {name: "csr"}, }; - constructor(private httpService: HttpService, private authenticationService: AuthenticationService) { - } - setupAcme() { const authHeader = this.authenticationService.getHeader(); this.httpService.requestUpdateTlsConfigFilesResource("key", authHeader, this.fileResources.key).subscribe(() => { diff --git a/client/app/src/pages/admin/network/https-status/https-status.component.html b/client/app/src/pages/admin/network/https-status/https-status.component.html index f21ba43115..46f2953cef 100644 --- a/client/app/src/pages/admin/network/https-status/https-status.component.html +++ b/client/app/src/pages/admin/network/https-status/https-status.component.html @@ -1,26 +1,34 @@ - -
+@if (tlsConfig) { +
+
+
+ {{'Status'|translate}} + : + @if (tlsConfig.enabled) { + + {{'Enabled'|translate}} + + } + @if (!tlsConfig.enabled) { + + {{'Disabled'|translate}} + + } +
+ @if (tlsConfig.acme) {
-
- {{'Status'|translate}} - : - - {{'Enabled'|translate}} - - - {{'Disabled'|translate}} - -
-
- {{'Auto - renewal'|translate}}: {{'Enabled'|translate}} -
+ {{'Auto - renewal'|translate}}: {{'Enabled'|translate}}
-
-
- {{'URL'|translate}} - : - https://{{networkResolver.dataModel.hostname}} -
+ } +
+
+ @if (tlsConfig.enabled) { + + }
- \ No newline at end of file +
+} \ No newline at end of file diff --git a/client/app/src/pages/admin/network/https-status/https-status.component.ts b/client/app/src/pages/admin/network/https-status/https-status.component.ts index 2b57aa5b5c..2638ed4625 100644 --- a/client/app/src/pages/admin/network/https-status/https-status.component.ts +++ b/client/app/src/pages/admin/network/https-status/https-status.component.ts @@ -1,21 +1,25 @@ -import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core"; +import { Component, EventEmitter, Input, OnInit, Output, inject } from "@angular/core"; import {NetworkResolver} from "@app/shared/resolvers/network.resolver"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {nodeResolverModel} from "@app/models/resolvers/node-resolver-model"; import {TlsConfig} from "@app/models/component-model/tls-confiq"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; + @Component({ - selector: "src-https-status", - templateUrl: "./https-status.component.html" + selector: "src-https-status", + templateUrl: "./https-status.component.html", + standalone: true, + imports: [TranslatorPipe] }) export class HttpsStatusComponent implements OnInit { + protected networkResolver = inject(NetworkResolver); + private nodeResolver = inject(NodeResolver); + @Output() dataToParent = new EventEmitter(); @Input() tlsConfig: TlsConfig; nodeData: nodeResolverModel; - constructor(protected networkResolver: NetworkResolver, private nodeResolver: NodeResolver) { - } - ngOnInit(): void { this.nodeData = this.nodeResolver.dataModel; } diff --git a/client/app/src/pages/admin/network/https/https.component.html b/client/app/src/pages/admin/network/https/https.component.html index 7baa3f4fbf..7d2cd8505d 100644 --- a/client/app/src/pages/admin/network/https/https.component.html +++ b/client/app/src/pages/admin/network/https/https.component.html @@ -11,16 +11,24 @@

-
-
-
+@if (appDataService.public.node.hostname) { +
+
+
-
- -
-
- + @switch (menuState) { + @case ('setup') { +
+ +
+ } + @default { +
+ +
+ } + }
-
\ No newline at end of file +} \ No newline at end of file diff --git a/client/app/src/pages/admin/network/https/https.component.ts b/client/app/src/pages/admin/network/https/https.component.ts index 95b054752d..0a8cd7a7b9 100644 --- a/client/app/src/pages/admin/network/https/https.component.ts +++ b/client/app/src/pages/admin/network/https/https.component.ts @@ -1,25 +1,35 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {AppDataService} from "@app/app-data.service"; import {TlsConfig} from "@app/models/component-model/tls-confiq"; import {Constants} from "@app/shared/constants/constants"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {HttpService} from "@app/shared/services/http.service"; import {UtilsService} from "@app/shared/services/utils.service"; +import { FormsModule } from "@angular/forms"; +import { NgClass } from "@angular/common"; +import { HttpsStatusComponent } from "../https-status/https-status.component"; +import { HttpsSetupComponent } from "../https-setup/https-setup.component"; +import { HttpsFilesComponent } from "../https-files/https-files.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-https", - templateUrl: "./https.component.html" + selector: "src-https", + templateUrl: "./https.component.html", + standalone: true, + imports: [FormsModule, NgClass, HttpsStatusComponent, HttpsSetupComponent, HttpsFilesComponent, TranslatorPipe] }) export class HttpsComponent implements OnInit { + protected nodeResolver = inject(NodeResolver); + private httpService = inject(HttpService); + private utilsService = inject(UtilsService); + protected appDataService = inject(AppDataService); + protected readonly Constants = Constants; state = 0; menuState = "setup"; tlsConfig: TlsConfig; hostName: string=""; - constructor(protected nodeResolver: NodeResolver, private httpService: HttpService, private utilsService: UtilsService,protected appDataService:AppDataService) { - } - ngOnInit() { this.initFunction(); this.hostName = this.appDataService.public.node.hostname diff --git a/client/app/src/pages/admin/network/network-routing.module.ts b/client/app/src/pages/admin/network/network-routing.module.ts deleted file mode 100644 index a322223411..0000000000 --- a/client/app/src/pages/admin/network/network-routing.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {NetworkComponent} from "@app/pages/admin/network/network.component"; - -const routes: Routes = [ - { - path: "", - component: NetworkComponent, - pathMatch: "full", - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class NetworkRoutingModule { -} \ No newline at end of file diff --git a/client/app/src/pages/admin/network/network.component.html b/client/app/src/pages/admin/network/network.component.html index 5bbc7579c9..3bf2fab240 100644 --- a/client/app/src/pages/admin/network/network.component.html +++ b/client/app/src/pages/admin/network/network.component.html @@ -1,25 +1,25 @@
- -
- - - - - - - - - - - - + +
+ + + + + + + + + + + +
\ No newline at end of file diff --git a/client/app/src/pages/admin/network/network.component.ts b/client/app/src/pages/admin/network/network.component.ts index 4b56ad0585..8fa94b8d8c 100644 --- a/client/app/src/pages/admin/network/network.component.ts +++ b/client/app/src/pages/admin/network/network.component.ts @@ -1,16 +1,24 @@ -import {Component, TemplateRef, ViewChild, AfterViewInit, ChangeDetectorRef} from "@angular/core"; +import { Component, TemplateRef, ViewChild, AfterViewInit, ChangeDetectorRef, inject } from "@angular/core"; import {Tab} from "@app/models/component-model/tab"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {HttpsComponent} from "@app/pages/admin/network/https/https.component"; import {TorComponent} from "@app/pages/admin/network/tor/tor.component"; import {AccessControlComponent} from "@app/pages/admin/network/access-control/access-control.component"; import {UrlRedirectsComponent} from "@app/pages/admin/network/url-redirects/url-redirects.component"; +import { NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgbNavOutlet } from "@ng-bootstrap/ng-bootstrap"; +import { NgTemplateOutlet } from "@angular/common"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-network", - templateUrl: "./network.component.html" + selector: "src-network", + templateUrl: "./network.component.html", + standalone: true, + imports: [NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgTemplateOutlet, NgbNavOutlet, HttpsComponent, TorComponent, AccessControlComponent, UrlRedirectsComponent, TranslatorPipe] }) export class NetworkComponent implements AfterViewInit { + node = inject(NodeResolver); + private cdr = inject(ChangeDetectorRef); + @ViewChild("tab1") tab1!: TemplateRef; @ViewChild("tab2") tab2!: TemplateRef; @ViewChild("tab3") tab3!: TemplateRef; @@ -20,12 +28,6 @@ export class NetworkComponent implements AfterViewInit { nodeData: NodeResolver; active: string; - constructor( - public node: NodeResolver, - private cdr: ChangeDetectorRef - ) { - } - ngAfterViewInit(): void { setTimeout(() => { this.active = "HTTPS"; diff --git a/client/app/src/pages/admin/network/network.module.ts b/client/app/src/pages/admin/network/network.module.ts deleted file mode 100644 index 557e9a0b7a..0000000000 --- a/client/app/src/pages/admin/network/network.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {NetworkRoutingModule} from "@app/pages/admin/network/network-routing.module"; -import {NetworkComponent} from "@app/pages/admin/network/network.component"; -import {FormsModule} from "@angular/forms"; -import {RouterModule} from "@angular/router"; -import {NgbNavModule, NgbModule} from "@ng-bootstrap/ng-bootstrap"; -import {NgSelectModule} from "@ng-select/ng-select"; -import {SharedModule} from "@app/shared.module"; -import {HttpsComponent} from "@app/pages/admin/network/https/https.component"; -import {TorComponent} from "@app/pages/admin/network/tor/tor.component"; -import {AccessControlComponent} from "@app/pages/admin/network/access-control/access-control.component"; -import {UrlRedirectsComponent} from "@app/pages/admin/network/url-redirects/url-redirects.component"; -import {HttpsSetupComponent} from "@app/pages/admin/network/https-setup/https-setup.component"; -import {HttpsFilesComponent} from "@app/pages/admin/network/https-files/https-files.component"; -import {HttpsStatusComponent} from "@app/pages/admin/network/https-status/https-status.component"; -import {HttpsCsrGenComponent} from "@app/pages/admin/network/https-csr-gen/https-csr-gen.component"; - - -@NgModule({ - declarations: [ - NetworkComponent, - HttpsComponent, - TorComponent, - AccessControlComponent, - UrlRedirectsComponent, - HttpsSetupComponent, - HttpsFilesComponent, - HttpsStatusComponent, - HttpsCsrGenComponent - ], - imports: [ - CommonModule, - NetworkRoutingModule, SharedModule, NgbNavModule, NgbModule, RouterModule, FormsModule, NgSelectModule, - ] -}) -export class NetworkModule { -} \ No newline at end of file diff --git a/client/app/src/pages/admin/network/tor/tor.component.html b/client/app/src/pages/admin/network/tor/tor.component.html index 7f05416422..8cc5cc1334 100644 --- a/client/app/src/pages/admin/network/tor/tor.component.html +++ b/client/app/src/pages/admin/network/tor/tor.component.html @@ -1,94 +1,98 @@ - -
-
- - {{ 'Tor Onion Service' | translate }} - - - - - -
+@if (networkData) { +
+
+ + {{ 'Tor Onion Service' | translate }} + + + + +
-
-
-
-
-
-
- - -
-
- - -
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{ 'Role' | translate }}{{ 'Enabled' | translate }}
{{ 'Admin' | translate }} - -
{{ 'Analyst' | translate }} - -
{{ 'Custodian' | translate }} - -
{{ 'Recipient' | translate }} - -
{{ 'Whistleblower' | translate }} - -
-
-
-
-
-
-
- -
+
+
+
+ +
+
+ @if (nodeResolver.dataModel.root_tenant) { +
+ + +
+ } +
+ + +
+ @if (networkData.reachable_via_web) { +
+
+
- + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ 'Role' | translate }}{{ 'Enabled' | translate }}
{{ 'Admin' | translate }} + +
{{ 'Analyst' | translate }} + +
{{ 'Custodian' | translate }} + +
{{ 'Recipient' | translate }} + +
{{ 'Whistleblower' | translate }} + +
+
+ } +
+
+
+
+
+ +
+
- \ No newline at end of file +
+} \ No newline at end of file diff --git a/client/app/src/pages/admin/network/tor/tor.component.ts b/client/app/src/pages/admin/network/tor/tor.component.ts index 9c5d1365c0..1a04900a41 100644 --- a/client/app/src/pages/admin/network/tor/tor.component.ts +++ b/client/app/src/pages/admin/network/tor/tor.component.ts @@ -1,20 +1,26 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {networkResolverModel} from "@app/models/resolvers/network-resolver-model"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {HttpService} from "@app/shared/services/http.service"; import {UtilsService} from "@app/shared/services/utils.service"; +import { FormsModule } from "@angular/forms"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; + @Component({ - selector: "src-tor", - templateUrl: "./tor.component.html" + selector: "src-tor", + templateUrl: "./tor.component.html", + standalone: true, + imports: [FormsModule, TranslatorPipe] }) export class TorComponent implements OnInit { + protected nodeResolver = inject(NodeResolver); + private httpService = inject(HttpService); + private utilsService = inject(UtilsService); + torOnionResetInProgress: boolean = false; networkData: networkResolverModel; - constructor(protected nodeResolver: NodeResolver, private httpService: HttpService, private utilsService: UtilsService) { - } - ngOnInit(): void { this.getNetortResolver(); } diff --git a/client/app/src/pages/admin/network/url-redirects/url-redirects.component.html b/client/app/src/pages/admin/network/url-redirects/url-redirects.component.html index 28790f4a6e..515d72cd0c 100644 --- a/client/app/src/pages/admin/network/url-redirects/url-redirects.component.html +++ b/client/app/src/pages/admin/network/url-redirects/url-redirects.component.html @@ -1,52 +1,54 @@
-
-
-
-
- - {{ 'From' | translate }} - - -
-
-
- - - {{ 'To' | translate }} - - - -
- -
-
+
+
+
+
+ + {{ 'From' | translate }} + + +
+
+
+ + + {{ 'To' | translate }} + + + +
+ +
+

-
- - - - - - - - - - - - - - - -
{{ 'From' | translate }}{{ 'To' | translate }}
{{redirectPath(redirect, 1)}}{{redirectPath(redirect, 2)}} - -
-
+
+ + + + + + + + + + @for (redirect of redirectData | orderBy:'path1'; track redirect) { + + + + + + } + +
{{ 'From' | translate }}{{ 'To' | translate }}
{{redirectPath(redirect, 1)}}{{redirectPath(redirect, 2)}} + +
+
\ No newline at end of file diff --git a/client/app/src/pages/admin/network/url-redirects/url-redirects.component.ts b/client/app/src/pages/admin/network/url-redirects/url-redirects.component.ts index 37e6e2d3a6..c6d814fa1a 100644 --- a/client/app/src/pages/admin/network/url-redirects/url-redirects.component.ts +++ b/client/app/src/pages/admin/network/url-redirects/url-redirects.component.ts @@ -1,21 +1,26 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {redirectResolverModel} from "@app/models/resolvers/redirect-resolver-model"; import {HttpService} from "@app/shared/services/http.service"; +import { FormsModule } from "@angular/forms"; + +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { OrderByPipe } from "@app/shared/pipes/order-by.pipe"; @Component({ - selector: "src-url-redirects", - templateUrl: "./url-redirects.component.html" + selector: "src-url-redirects", + templateUrl: "./url-redirects.component.html", + standalone: true, + imports: [FormsModule, TranslatorPipe, OrderByPipe] }) export class UrlRedirectsComponent implements OnInit { + private httpService = inject(HttpService); + redirectData: redirectResolverModel[]; new_redirect = { path1: "", path2: "" }; - constructor(private httpService: HttpService) { - } - ngOnInit(): void { this.getResolver(); } diff --git a/client/app/src/pages/admin/notifications/notification-tab1/notification-tab1.component.html b/client/app/src/pages/admin/notifications/notification-tab1/notification-tab1.component.html index b322840049..8a4a736721 100644 --- a/client/app/src/pages/admin/notifications/notification-tab1/notification-tab1.component.html +++ b/client/app/src/pages/admin/notifications/notification-tab1/notification-tab1.component.html @@ -1,102 +1,108 @@
-
- - {{ 'Default mail configuration in use. Please consider using a private mail server.' | translate }} + @if (notificationResolver.dataModel.smtp_server === 'mail.globaleaks.org') { +
+ + {{ 'Default mail configuration in use. Please consider using a private mail server.' | translate }}
+ }
-
-
- - -
-
- {{ 'Invalid email address' | translate }} -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- - -
-
- - -
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - -
{{ 'Role' | translate }}{{ 'Enabled' | translate }}
{{ 'Admin' | translate }} - -
{{ 'Analyst' | translate }} - -
{{ 'Custodian' | translate }} - -
{{ 'Recipient' | translate }} - -
+
+
+ + +
+ @if (source_email?.errors?.['pattern']) { +
+ {{ 'Invalid email address' | translate }} +
+ } +
+ + +
+
+ + +
+
+ + +
+
+ + +
+ @if (notificationResolver.dataModel.smtp_authentication) { +
- - + +
- - - - + +
+
+ } +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
{{ 'Role' | translate }}{{ 'Enabled' | translate }}
{{ 'Admin' | translate }} + +
{{ 'Analyst' | translate }} + +
{{ 'Custodian' | translate }} + +
{{ 'Recipient' | translate }} + +
+
+ + +
+
+ + + +
+
diff --git a/client/app/src/pages/admin/notifications/notification-tab1/notification-tab1.component.ts b/client/app/src/pages/admin/notifications/notification-tab1/notification-tab1.component.ts index a74f14cc27..a1c5806314 100644 --- a/client/app/src/pages/admin/notifications/notification-tab1/notification-tab1.component.ts +++ b/client/app/src/pages/admin/notifications/notification-tab1/notification-tab1.component.ts @@ -1,5 +1,5 @@ -import {Component, Input} from "@angular/core"; -import {NgForm} from "@angular/forms"; +import { Component, Input, inject } from "@angular/core"; +import { NgForm, FormsModule } from "@angular/forms"; import {notificationResolverModel} from "@app/models/resolvers/notification-resolver-model"; import {Constants} from "@app/shared/constants/constants"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; @@ -7,17 +7,22 @@ import {NotificationsResolver} from "@app/shared/resolvers/notifications.resolve import {UtilsService} from "@app/shared/services/utils.service"; import {switchMap} from "rxjs"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; + @Component({ - selector: "src-notification-tab1", - templateUrl: "./notification-tab1.component.html" + selector: "src-notification-tab1", + templateUrl: "./notification-tab1.component.html", + standalone: true, + imports: [FormsModule, TranslatorPipe] }) export class NotificationTab1Component { + protected nodeResolver = inject(NodeResolver); + protected notificationResolver = inject(NotificationsResolver); + private utilsService = inject(UtilsService); + @Input() notificationForm: NgForm; protected readonly Constants = Constants; - constructor(protected nodeResolver: NodeResolver, protected notificationResolver: NotificationsResolver, private utilsService: UtilsService) { - } - updateNotification(notification: notificationResolverModel) { this.utilsService.updateAdminNotification(notification).subscribe(_ => { this.utilsService.reloadComponent(); diff --git a/client/app/src/pages/admin/notifications/notification-tab2/notification-tab2.component.html b/client/app/src/pages/admin/notifications/notification-tab2/notification-tab2.component.html index 5b6407c1ea..c6961268ec 100644 --- a/client/app/src/pages/admin/notifications/notification-tab2/notification-tab2.component.html +++ b/client/app/src/pages/admin/notifications/notification-tab2/notification-tab2.component.html @@ -1,25 +1,29 @@
-
-
- - -
-
-
-
-
-
- -
-
- -
-
-
+
+
+ +
+
+
+ @if (template) { +
+
+
+ +
+
+ +
+
+
+ } +
\ No newline at end of file diff --git a/client/app/src/pages/admin/notifications/notification-tab2/notification-tab2.component.ts b/client/app/src/pages/admin/notifications/notification-tab2/notification-tab2.component.ts index 00a2b6449a..b7b617cdaf 100644 --- a/client/app/src/pages/admin/notifications/notification-tab2/notification-tab2.component.ts +++ b/client/app/src/pages/admin/notifications/notification-tab2/notification-tab2.component.ts @@ -1,21 +1,25 @@ -import {Component, Input, OnInit} from "@angular/core"; -import {NgForm} from "@angular/forms"; +import { Component, Input, OnInit, inject } from "@angular/core"; +import { NgForm, FormsModule } from "@angular/forms"; import {notificationResolverModel} from "@app/models/resolvers/notification-resolver-model"; import {NotificationsResolver} from "@app/shared/resolvers/notifications.resolver"; import {UtilsService} from "@app/shared/services/utils.service"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; + @Component({ - selector: "src-notification-tab2", - templateUrl: "./notification-tab2.component.html" + selector: "src-notification-tab2", + templateUrl: "./notification-tab2.component.html", + standalone: true, + imports: [FormsModule, TranslatorPipe] }) export class NotificationTab2Component implements OnInit { + private notificationResolver = inject(NotificationsResolver); + private utilsService = inject(UtilsService); + @Input() notificationForm: NgForm; template: string; notificationData: notificationResolverModel; - constructor(private notificationResolver: NotificationsResolver, private utilsService: UtilsService) { - } - ngOnInit(): void { this.notificationData = this.notificationResolver.dataModel; } diff --git a/client/app/src/pages/admin/notifications/notifications-routing.module.ts b/client/app/src/pages/admin/notifications/notifications-routing.module.ts deleted file mode 100644 index c1ded70e7d..0000000000 --- a/client/app/src/pages/admin/notifications/notifications-routing.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {NotificationsComponent} from "@app/pages/admin/notifications/notifications.component"; - -const routes: Routes = [ - { - path: "", - component: NotificationsComponent, - pathMatch: "full", - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class NotificationsRoutingModule { -} \ No newline at end of file diff --git a/client/app/src/pages/admin/notifications/notifications.component.html b/client/app/src/pages/admin/notifications/notifications.component.html index 661d337e62..b6a248c946 100644 --- a/client/app/src/pages/admin/notifications/notifications.component.html +++ b/client/app/src/pages/admin/notifications/notifications.component.html @@ -1,19 +1,19 @@
- -
- - - - - - + +
+ + + + + +
\ No newline at end of file diff --git a/client/app/src/pages/admin/notifications/notifications.component.ts b/client/app/src/pages/admin/notifications/notifications.component.ts index 3299adfd85..4e2e4fa5df 100644 --- a/client/app/src/pages/admin/notifications/notifications.component.ts +++ b/client/app/src/pages/admin/notifications/notifications.component.ts @@ -1,14 +1,23 @@ -import {Component, TemplateRef, ViewChild, AfterViewInit, ChangeDetectorRef} from "@angular/core"; +import { Component, TemplateRef, ViewChild, AfterViewInit, ChangeDetectorRef, inject } from "@angular/core"; import {Tab} from "@app/models/component-model/tab"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {NotificationTab1Component} from "@app/pages/admin/notifications/notification-tab1/notification-tab1.component"; import {NotificationTab2Component} from "@app/pages/admin/notifications/notification-tab2/notification-tab2.component"; +import { FormsModule } from "@angular/forms"; +import { NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgbNavOutlet } from "@ng-bootstrap/ng-bootstrap"; +import { NgTemplateOutlet } from "@angular/common"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-notifications", - templateUrl: "./notifications.component.html" + selector: "src-notifications", + templateUrl: "./notifications.component.html", + standalone: true, + imports: [FormsModule, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgTemplateOutlet, NgbNavOutlet, NotificationTab1Component, NotificationTab2Component, TranslatorPipe] }) export class NotificationsComponent implements AfterViewInit { + protected node = inject(NodeResolver); + private cdr = inject(ChangeDetectorRef); + @ViewChild("tab1") tab1!: TemplateRef; @ViewChild("tab2") tab2!: TemplateRef; @@ -16,9 +25,6 @@ export class NotificationsComponent implements AfterViewInit { nodeData: NodeResolver; active: string; - constructor(protected node: NodeResolver, private cdr: ChangeDetectorRef) { - } - ngAfterViewInit(): void { setTimeout(() => { this.active = "Settings"; diff --git a/client/app/src/pages/admin/notifications/notifications.module.ts b/client/app/src/pages/admin/notifications/notifications.module.ts deleted file mode 100644 index 5540216db2..0000000000 --- a/client/app/src/pages/admin/notifications/notifications.module.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {NotificationsRoutingModule} from "@app/pages/admin/notifications/notifications-routing.module"; -import {NotificationsComponent} from "@app/pages/admin/notifications/notifications.component"; -import {FormsModule} from "@angular/forms"; -import {RouterModule} from "@angular/router"; -import {NgbNavModule, NgbModule} from "@ng-bootstrap/ng-bootstrap"; -import {NgSelectModule} from "@ng-select/ng-select"; -import {SharedModule} from "@app/shared.module"; -import {NotificationTab1Component} from "@app/pages/admin/notifications/notification-tab1/notification-tab1.component"; -import {NotificationTab2Component} from "@app/pages/admin/notifications/notification-tab2/notification-tab2.component"; - - -@NgModule({ - declarations: [ - NotificationsComponent, - NotificationTab1Component, - NotificationTab2Component - ], - imports: [ - CommonModule, - NotificationsRoutingModule, SharedModule, NgbNavModule, NgbModule, RouterModule, FormsModule, NgSelectModule, - ] -}) -export class NotificationsModule { -} \ No newline at end of file diff --git a/client/app/src/pages/admin/questionnaires/add-field-from-template/add-field-from-template.component.html b/client/app/src/pages/admin/questionnaires/add-field-from-template/add-field-from-template.component.html index d19d9ad2a5..22b53df343 100644 --- a/client/app/src/pages/admin/questionnaires/add-field-from-template/add-field-from-template.component.html +++ b/client/app/src/pages/admin/questionnaires/add-field-from-template/add-field-from-template.component.html @@ -1,17 +1,21 @@
-
- - -
- {{ 'This field is mandatory' | translate }} -
-
-
- -
+
+ + + @if (newField.form.get('template_id')?.errors?.['required']) { +
+ {{ 'This field is mandatory' | translate }} +
+ } +
+
+ +
\ No newline at end of file diff --git a/client/app/src/pages/admin/questionnaires/add-field-from-template/add-field-from-template.component.ts b/client/app/src/pages/admin/questionnaires/add-field-from-template/add-field-from-template.component.ts index 162c52c316..81ac232e88 100644 --- a/client/app/src/pages/admin/questionnaires/add-field-from-template/add-field-from-template.component.ts +++ b/client/app/src/pages/admin/questionnaires/add-field-from-template/add-field-from-template.component.ts @@ -1,16 +1,26 @@ -import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core"; +import { Component, EventEmitter, Input, OnInit, Output, inject } from "@angular/core"; import {HttpService} from "@app/shared/services/http.service"; import {UtilsService} from "@app/shared/services/utils.service"; import {NewField} from "@app/models/admin/new-field"; import {QuestionnaireService} from "@app/pages/admin/questionnaires/questionnaire.service"; import {Step} from "@app/models/resolvers/questionnaire-model"; import {Field, fieldtemplatesResolverModel} from "@app/models/resolvers/field-template-model"; +import { FormsModule } from "@angular/forms"; + +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-add-field-from-template", - templateUrl: "./add-field-from-template.component.html" + selector: "src-add-field-from-template", + templateUrl: "./add-field-from-template.component.html", + standalone: true, + imports: [FormsModule, TranslatorPipe, TranslateModule] }) export class AddFieldFromTemplateComponent implements OnInit { + private questionnaireService = inject(QuestionnaireService); + private httpService = inject(HttpService); + private utilsService = inject(UtilsService); + @Input() fieldTemplatesData: fieldtemplatesResolverModel[]; @Input() step: Step; @Input() type: string; @@ -19,9 +29,6 @@ export class AddFieldFromTemplateComponent implements OnInit { fields: Step[] | Field[]; new_field: { template_id: string } = {template_id: ""}; - constructor(private questionnaireService: QuestionnaireService, private httpService: HttpService, private utilsService: UtilsService) { - } - ngOnInit(): void { if (this.step) { this.fields = this.step.children; diff --git a/client/app/src/pages/admin/questionnaires/add-field/add-field.component.html b/client/app/src/pages/admin/questionnaires/add-field/add-field.component.html index 6146b853f7..ab52d83e7d 100644 --- a/client/app/src/pages/admin/questionnaires/add-field/add-field.component.html +++ b/client/app/src/pages/admin/questionnaires/add-field/add-field.component.html @@ -1,34 +1,38 @@
-
- - -
- {{ 'This field is mandatory' | translate }} -
-
-
- - -
- {{ 'This field is mandatory' | translate }} -
-
-
- -
+
+ + + @if (newField.form.get('label')?.errors?.['required']) { +
+ {{ 'This field is mandatory' | translate }} +
+ } +
+
+ + + @if (newField.form.get('type')?.errors?.['required']) { +
+ {{ 'This field is mandatory' | translate }} +
+ } +
+
+ +
\ No newline at end of file diff --git a/client/app/src/pages/admin/questionnaires/add-field/add-field.component.ts b/client/app/src/pages/admin/questionnaires/add-field/add-field.component.ts index 8b31bb0ea1..fce1ad2a8a 100644 --- a/client/app/src/pages/admin/questionnaires/add-field/add-field.component.ts +++ b/client/app/src/pages/admin/questionnaires/add-field/add-field.component.ts @@ -1,4 +1,4 @@ -import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core"; +import { Component, EventEmitter, Input, OnInit, Output, inject } from "@angular/core"; import {HttpService} from "@app/shared/services/http.service"; import {UtilsService} from "@app/shared/services/utils.service"; import {NewField} from "@app/models/admin/new-field"; @@ -6,21 +6,28 @@ import {FieldTemplate} from "@app/models/admin/field-Template"; import {QuestionnaireService} from "@app/pages/admin/questionnaires/questionnaire.service"; import {Step} from "@app/models/resolvers/questionnaire-model"; import {Field} from "@app/models/resolvers/field-template-model"; +import { FormsModule } from "@angular/forms"; + +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-add-field", - templateUrl: "./add-field.component.html" + selector: "src-add-field", + templateUrl: "./add-field.component.html", + standalone: true, + imports: [FormsModule, TranslatorPipe, TranslateModule] }) export class AddFieldComponent implements OnInit { + private questionnaireService = inject(QuestionnaireService); + private httpService = inject(HttpService); + private utilsService = inject(UtilsService); + @Output() dataToParent = new EventEmitter(); @Input() step: Step; @Input() type: string; new_field: { label: string, type: string } = {label: "", type: ""}; fields: Step[] | Field[]; - constructor(private questionnaireService: QuestionnaireService, private httpService: HttpService, private utilsService: UtilsService) { - } - ngOnInit(): void { if (this.step) { this.fields = this.step.children; diff --git a/client/app/src/pages/admin/questionnaires/fields/fields.component.html b/client/app/src/pages/admin/questionnaires/fields/fields.component.html index 6c5c9d553b..f9ce4379d9 100644 --- a/client/app/src/pages/admin/questionnaires/fields/fields.component.html +++ b/client/app/src/pages/admin/questionnaires/fields/fields.component.html @@ -1,433 +1,594 @@
-
-
- {{ field.label }} - - {{ 'Row' | translate }}: {{ field.y }} - {{ 'Column' | translate }}: {{ field.x }} - {{ 'Width' | translate }}: {{ field.width }} - - - {{ 'Type' | translate }}: - - {{ 'Single-line text input' | translate }} - {{ 'Multi-line text input' | translate }} - {{ 'Selection box' | translate }} - {{ 'Multiple choice input' | translate }} - {{ 'Checkbox' | translate }} - {{ 'Attachment' | translate }} - {{ 'Terms of service' | translate }} - {{ 'Date' | translate }} - {{ 'Date range' | translate }} - {{ 'Voice' | translate }} - {{ 'Question group' | translate }} - {{ field.type }} - - {{ 'Template' | translate }} - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - +
+
+ {{ field.label }} + @if (field.step_id || field.fieldgroup_id) { + + {{ 'Row' | translate }}: {{ field.y }} + {{ 'Column' | translate }}: {{ field.x }} + {{ 'Width' | translate }}: {{ field.width }} + } + + {{ 'Type' | translate }}: + @if (!field.template_id) { + @switch (field.type) { + @case ('inputbox') { + {{ 'Single-line text input' | translate }} + } + @case ('textarea') { + {{ 'Multi-line text input' | translate }} + } + @case ('selectbox') { + {{ 'Selection box' | translate }} + } + @case ('multichoice') { + {{ 'Multiple choice input' | translate }} + } + @case ('checkbox') { + {{ 'Checkbox' | translate }} + } + @case ('fileupload') { + {{ 'Attachment' | translate }} + } + @case ('tos') { + {{ 'Terms of service' | translate }} + } + @case ('date') { + {{ 'Date' | translate }} + } + @case ('daterange') { + {{ 'Date range' | translate }} + } + @case ('voice') { + {{ 'Voice' | translate }} + } + @case ('fieldgroup') { + {{ 'Question group' | translate }} + } + @default { + {{ field.type }} + } + } + } + @if (field.template_id) { + {{ 'Template' | translate }} + } + + @if (field.multi_entry) { + + @switch (field.type) { + @case ('fileupload') { + + + + } + @default { + + + + } + } + + } + @if (field.required) { + + + + + + }
-
-
-
-
-
- - -
- {{ 'This field is mandatory' | translate }} -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- - - -
-
-
- - + @if (field.editable) { + + + @if (!editing) { + + } + @if (editing) { + + } + @if (editing) { + + } + + @if (field.editable) { + + } + @if (field.step_id || field.fieldgroup_id) { + + + + + + + } + + + } +
+ @if (editing) { +
+
+
+ @if (field.editable) { +
+ @if (field.instance !== 'reference') { +
+ + + @if (editField.form.get('label')?.errors?.['required']) { +
+ {{ 'This field is mandatory' | translate }} +
+ } +
+ } + @if (field.instance !== 'reference' && (field.type === 'inputbox' || field.type === 'textarea')) { +
+ + +
+ } + @if (field.instance !== 'reference' && (field.type === 'selectbox' || field.type === 'checkbox' || field.type === 'multichoice')) { +
+ + +
+ } + @if (field.instance !== 'reference') { +
+ + +
+ } + @if (field.instance !== 'reference') { +
+ + +
+ } + @if (field.instance !== 'reference' && (field.type === 'inputbox' || field.type === 'textarea')) { +
+ + +
+ } + @if (field.type !== 'fieldgroup') { +
+ + +
+ } + @if ((field.type === 'fieldgroup' || field.type === 'fileupload') && field.template_id === '') { +
+
+ @switch (typeSwitch(field.type)) { + @case ('fileupload') { + + + } + @default { + + + } + }
+
+ } + @if (fieldIsMarkableSubjectToPreview) { +
+ + +
+ } +
+ + +
+ @if (field.template_id) { +
+ + +
+ } +
+ } +
+ @if (field.instance !== 'reference') { +
+
+ @if (field.type === 'inputbox' || field.type === 'textarea' || field.type === 'voice') { +
+
+ + +
{{ 'Set the value to 0 to disable this feature.' | translate }}
+
+
+ + +
{{ 'Set the value to 0 to disable this feature.' | translate }}
+
+ @if (field.attrs.input_validation) {
- - + +
-
- - + } + @if (isCustomValidation(field)) { +
+ +
+ }
-
-
-
-
-
- - -
{{ 'Set the value to 0 to disable this feature.' | translate }}
-
-
- - -
{{ 'Set the value to 0 to disable this feature.' | translate }}
-
-
- - -
-
- - -
+ } + @if (field.type === 'tos') { +
+
+ + +
+
+ + +
+
+ + +
+ @if (field.attrs.attachment.value) { +
+
+ + +
+
+ + +
-
-
- - -
+ } +
+ } + @if (field.type === 'fieldgroup') { +
+
+ + + @if (field.attrs.multimedia.value) { +
- - + +
- - -
-
-
- - -
-
- - -
+ +
+
+ } +
+
+ } +
+ @if (field.type === 'checkbox' || field.type === 'selectbox' || field.type === 'multichoice') { +
+
+ +
-
-
- - -
-
- - -
-
- - -
-
-
+
+ } + @if (field.type === 'date' || field.type === 'daterange') { +
+
+ + + @if (field.attrs.min_date.value) { + + {{minDateFormat(field.attrs.min_date.value) | date:'dd-MM-yyyy'}} + + + } + @if (field.attrs.min_date.value === '') { + + }
-
-
-
- - -
-
-
-
- - - - {{minDateFormat(field.attrs.min_date.value) | date:'dd-MM-yyyy'}} - - - -
-
- - - - {{maxDateFormat(field.attrs.max_date.value) | date:'dd-MM-yyyy' }} - - - -
-
+
+ + + @if (field.attrs.max_date.value) { + + {{maxDateFormat(field.attrs.max_date.value) | date:'dd-MM-yyyy' }} + + + } + @if (field.attrs.max_date.value === '') { + + }
-
+
+ } +
-
-
-
+
+ } +
+ @if (field.instance === 'reference') { +
+ @switch (field.template_id) { + @case ('whistleblower_identity') { +
+
+ + +
+ @if (!field.required) { +
- - -
-
-
- -
- -
-
+ +
+ +
-
-
+
+ } +
+ } + }
-
-
-
- - + } +
+
+
+ @if (nodeResolver.dataModel.enable_scoring_system) { +
+ + +
+ } +
+ + @if (showAddTrigger) { +
+
+ +
+ @if (new_trigger.field) { +
+
+ } + @if (new_trigger.field) {
- -
-
- -
-
- -
-
- - -
-
- -
-
-
- - {{parsedFields.fields_by_id[trigger.field].label}}: {{parsedFields.options_by_id[trigger.option].label}} - ( {{'Sufficient'|translate}}) -
+ +
+ } +
+ +
-
-
-
-
- -
-
-
-
-
{{ i + 1 }}
-
-
- -
-
- - - - - - - - - - - - - - - -
-
-
-
-
+ } + @for (trigger of field.triggered_by_options; track trigger) { +
+ + {{parsedFields.fields_by_id[trigger.field].label}}: {{parsedFields.options_by_id[trigger.option].label}} + @if (trigger.sufficient) { + ( {{'Sufficient'|translate}}) + }
+ }
-
+
+
+
+
+ @if (showOptions(field)) { +
+
-
-
- {{ 'Questions' | translate }} - - + @for (option of field.options; track option; let i = $index) { +
+
+
+
{{ i + 1 }}
-
-
- -
-
- -
+
+
-
-
-
-
- -
-
-
+
+ + + + + + + + + + + + + @if (nodeResolver.dataModel.enable_scoring_system) { + + + + }
+
+ }
-
-
UUID: {{field.id}}
+
+ } +
-
-
\ No newline at end of file + @if (field.editable && field.instance !== 'reference' && field.type === 'fieldgroup') { +
+
+
+
+ {{ 'Questions' | translate }} + + @if (fieldTemplatesData.length > 0) { + + } +
+ @if (field.editable && field.type === 'fieldgroup') { +
+ @if (showAddQuestion) { +
+ +
+ } + @if (showAddQuestionFromTemplate) { +
+ +
+ } +
+ } +
+ @for (childField of children | orderBy:['y', 'x']; track childField; let index = $index) { +
+
+
+ +
+
+
+ } +
+
+
+
+ } + @if (field.instance === 'template') { +
UUID: {{field.id}}
+ } +
+ } +
+
\ No newline at end of file diff --git a/client/app/src/pages/admin/questionnaires/fields/fields.component.ts b/client/app/src/pages/admin/questionnaires/fields/fields.component.ts index 2d1b3e3124..045699dd32 100644 --- a/client/app/src/pages/admin/questionnaires/fields/fields.component.ts +++ b/client/app/src/pages/admin/questionnaires/fields/fields.component.ts @@ -1,6 +1,6 @@ -import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core"; -import {NgForm} from "@angular/forms"; -import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; +import { Component, EventEmitter, Input, OnInit, Output, inject } from "@angular/core"; +import { NgForm, FormsModule } from "@angular/forms"; +import { NgbModal, NgbInputDatepicker } from "@ng-bootstrap/ng-bootstrap"; import {AddOptionHintComponent} from "@app/shared/modals/add-option-hint/add-option-hint.component"; import {AssignScorePointsComponent} from "@app/shared/modals/assign-score-points/assign-score-points.component"; import {DeleteConfirmationComponent} from "@app/shared/modals/delete-confirmation/delete-confirmation.component"; @@ -17,12 +17,29 @@ import {ParsedFields} from "@app/models/component-model/parsedFields"; import {Field, fieldtemplatesResolverModel} from "@app/models/resolvers/field-template-model"; import {Children, Option, TriggeredByOption} from "@app/models/app/shared-public-model"; import {AuthenticationService} from "@app/services/helper/authentication.service"; +import { NgClass, DatePipe } from "@angular/common"; +import { AddFieldComponent } from "../add-field/add-field.component"; +import { AddFieldFromTemplateComponent } from "../add-field-from-template/add-field-from-template.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { OrderByPipe } from "@app/shared/pipes/order-by.pipe"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-fields", - templateUrl: "./fields.component.html" + selector: "src-fields", + templateUrl: "./fields.component.html", + standalone: true, + imports: [FormsModule, NgbInputDatepicker, NgClass, AddFieldComponent, AddFieldFromTemplateComponent, DatePipe, TranslatorPipe, OrderByPipe, TranslateModule] }) export class FieldsComponent implements OnInit { + private authenticationService = inject(AuthenticationService); + private questionnaireService = inject(QuestionnaireService); + private modalService = inject(NgbModal); + nodeResolver = inject(NodeResolver); + private httpService = inject(HttpService); + private utilsService = inject(UtilsService); + private fieldTemplates = inject(FieldTemplatesResolver); + private fieldUtilities = inject(FieldUtilitiesService); + @Input() editField: NgForm; @Input() field: Children | Step | Field; @Input() fields: Children[] | Field[] | Step[]; @@ -48,9 +65,6 @@ export class FieldsComponent implements OnInit { }; instance: string; - constructor(private authenticationService: AuthenticationService,private questionnaireService: QuestionnaireService, private modalService: NgbModal, public nodeResolver: NodeResolver, private httpService: HttpService, private utilsService: UtilsService, private fieldTemplates: FieldTemplatesResolver, private fieldUtilities: FieldUtilitiesService,) { - } - ngOnInit(): void { if (Array.isArray(this.fieldTemplates.dataModel)) { this.fieldTemplatesData = this.fieldTemplates.dataModel; @@ -340,4 +354,8 @@ export class FieldsComponent implements OnInit { listenToAddFieldFormTemplate() { this.showAddQuestionFromTemplate = false; } + + isCustomValidation(field: Step | Field): boolean { + return field?.attrs?.input_validation?.value === 'custom'; + } } diff --git a/client/app/src/pages/admin/questionnaires/main/main.component.html b/client/app/src/pages/admin/questionnaires/main/main.component.html index e16b9d17e6..462d0c340b 100644 --- a/client/app/src/pages/admin/questionnaires/main/main.component.html +++ b/client/app/src/pages/admin/questionnaires/main/main.component.html @@ -1,43 +1,47 @@
-
-
- {{ 'Questionnaires' | translate }} - + + + + +
+ @if (showAddQuestionnaire) { +
+
+
+ +
+ +
+
+
+ - - - - -
-
- -
- -
- -
-
-
- -
- +
+ +
+ } + @if (questionnairesData.length > 0) { + @for (questionnaire of questionnairesData | orderBy:'name'; track trackByFn(index, questionnaire); let index = $index) { +
+
+ +
- -
-
- -
-
-
-
+ } + } +
\ No newline at end of file diff --git a/client/app/src/pages/admin/questionnaires/main/main.component.ts b/client/app/src/pages/admin/questionnaires/main/main.component.ts index 40541035f8..ef1d2af4bd 100644 --- a/client/app/src/pages/admin/questionnaires/main/main.component.ts +++ b/client/app/src/pages/admin/questionnaires/main/main.component.ts @@ -1,5 +1,5 @@ import {HttpClient} from "@angular/common/http"; -import {ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit, ViewChild} from "@angular/core"; +import { ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit, ViewChild, inject } from "@angular/core"; import {questionnaireResolverModel} from "@app/models/resolvers/questionnaire-model"; import {QuestionnairesResolver} from "@app/shared/resolvers/questionnaires.resolver"; import {HttpService} from "@app/shared/services/http.service"; @@ -7,12 +7,27 @@ import {UtilsService} from "@app/shared/services/utils.service"; import {NewQuestionare} from "@app/models/admin/new-questionare"; import {QuestionnaireService} from "@app/pages/admin/questionnaires/questionnaire.service"; import {Subject, takeUntil} from "rxjs"; +import { NgClass } from "@angular/common"; +import { FormsModule } from "@angular/forms"; +import { QuestionnairesListComponent } from "../questionnaires-list/questionnaires-list.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { OrderByPipe } from "@app/shared/pipes/order-by.pipe"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-main", - templateUrl: "./main.component.html" + selector: "src-main", + templateUrl: "./main.component.html", + standalone: true, + imports: [FormsModule, NgClass, QuestionnairesListComponent, TranslatorPipe, OrderByPipe, TranslateModule] }) export class MainComponent implements OnInit, OnDestroy { + private http = inject(HttpClient); + private questionnaireService = inject(QuestionnaireService); + private httpService = inject(HttpService); + private utilsService = inject(UtilsService); + private cdr = inject(ChangeDetectorRef); + protected questionnairesResolver = inject(QuestionnairesResolver); + private destroy$ = new Subject(); questionnairesData: questionnaireResolverModel[] = []; @@ -20,9 +35,6 @@ export class MainComponent implements OnInit, OnDestroy { showAddQuestionnaire: boolean = false; @ViewChild('keyUploadInput') keyUploadInput: ElementRef; - constructor(private http: HttpClient, private questionnaireService: QuestionnaireService, private httpService: HttpService, private utilsService: UtilsService, private cdr: ChangeDetectorRef, protected questionnairesResolver: QuestionnairesResolver) { - } - ngOnInit(): void { this.questionnaireService.sharedData = "step"; this.questionnaireService.getData().pipe(takeUntil(this.destroy$)).subscribe(() => { diff --git a/client/app/src/pages/admin/questionnaires/questionnaires-list/questionnaires-list.component.html b/client/app/src/pages/admin/questionnaires/questionnaires-list/questionnaires-list.component.html index 1f7c260bfd..51d92ecc72 100644 --- a/client/app/src/pages/admin/questionnaires/questionnaires-list/questionnaires-list.component.html +++ b/client/app/src/pages/admin/questionnaires/questionnaires-list/questionnaires-list.component.html @@ -1,44 +1,56 @@
- {{ questionnaire.name }} - - - - - - - - - + {{ questionnaire.name }} + + + @if (questionnaire.editable && !editing) { + + } + @if (editing) { + + } + @if (editing) { + + } + + + @if (questionnaire.editable) { + + } +
-
+@if (editing) { +

-
-
- - -
- {{ 'This field is mandatory' | translate }} -
+
+
+ + + @if (editQuestionnaire.form.get('name')?.errors?.['required']) { +
+ {{ 'This field is mandatory' | translate }}
+ }
-
- -
+
+
+ +
-
\ No newline at end of file +
+} \ No newline at end of file diff --git a/client/app/src/pages/admin/questionnaires/questionnaires-list/questionnaires-list.component.ts b/client/app/src/pages/admin/questionnaires/questionnaires-list/questionnaires-list.component.ts index 4e77921007..a4389d132e 100644 --- a/client/app/src/pages/admin/questionnaires/questionnaires-list/questionnaires-list.component.ts +++ b/client/app/src/pages/admin/questionnaires/questionnaires-list/questionnaires-list.component.ts @@ -1,5 +1,5 @@ -import {Component, Input} from "@angular/core"; -import {NgForm} from "@angular/forms"; +import { Component, Input, inject } from "@angular/core"; +import { NgForm, FormsModule } from "@angular/forms"; import {DeleteConfirmationComponent} from "@app/shared/modals/delete-confirmation/delete-confirmation.component"; import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; import { @@ -12,19 +12,28 @@ import {Observable} from "rxjs"; import {questionnaireResolverModel} from "@app/models/resolvers/questionnaire-model"; import {AuthenticationService} from "@app/services/helper/authentication.service"; +import { StepsComponent } from "../steps/steps.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; + @Component({ - selector: "src-questionnaires-list", - templateUrl: "./questionnaires-list.component.html" + selector: "src-questionnaires-list", + templateUrl: "./questionnaires-list.component.html", + standalone: true, + imports: [FormsModule, StepsComponent, TranslatorPipe, TranslateModule] }) export class QuestionnairesListComponent { + private authenticationService = inject(AuthenticationService); + private questionnaireService = inject(QuestionnaireService); + private modalService = inject(NgbModal); + private httpService = inject(HttpService); + private utilsService = inject(UtilsService); + @Input() questionnaire: questionnaireResolverModel; @Input() questionnaires: questionnaireResolverModel[]; @Input() editQuestionnaire: NgForm; editing: boolean = false; - constructor(private authenticationService: AuthenticationService, private questionnaireService: QuestionnaireService, private modalService: NgbModal, private httpService: HttpService, private utilsService: UtilsService) { - } - toggleEditing(questionnaire: questionnaireResolverModel) { this.editing = questionnaire.editable && !this.editing; } diff --git a/client/app/src/pages/admin/questionnaires/questionnaires-routing.module.ts b/client/app/src/pages/admin/questionnaires/questionnaires-routing.module.ts deleted file mode 100644 index fcb40fe02a..0000000000 --- a/client/app/src/pages/admin/questionnaires/questionnaires-routing.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {QuestionnairesComponent} from "@app/pages/admin/questionnaires/questionnaires.component"; - -const routes: Routes = [ - { - path: "", - component: QuestionnairesComponent, - pathMatch: "full", - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class QuestionnairesRoutingModule { -} \ No newline at end of file diff --git a/client/app/src/pages/admin/questionnaires/questionnaires.component.html b/client/app/src/pages/admin/questionnaires/questionnaires.component.html index 4c61edaf81..83a9488680 100644 --- a/client/app/src/pages/admin/questionnaires/questionnaires.component.html +++ b/client/app/src/pages/admin/questionnaires/questionnaires.component.html @@ -1,19 +1,19 @@
- -
- - - - - - + +
+ + + + + +
\ No newline at end of file diff --git a/client/app/src/pages/admin/questionnaires/questionnaires.component.ts b/client/app/src/pages/admin/questionnaires/questionnaires.component.ts index 46662b3844..9bbab90000 100644 --- a/client/app/src/pages/admin/questionnaires/questionnaires.component.ts +++ b/client/app/src/pages/admin/questionnaires/questionnaires.component.ts @@ -1,23 +1,29 @@ -import {Component, TemplateRef, ViewChild, AfterViewInit, ChangeDetectorRef} from "@angular/core"; +import { Component, TemplateRef, ViewChild, AfterViewInit, ChangeDetectorRef, inject } from "@angular/core"; import {Tab} from "@app/models/component-model/tab"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {MainComponent} from "@app/pages/admin/questionnaires/main/main.component"; import {QuestionsComponent} from "@app/pages/admin/questionnaires/questions/questions.component"; +import { NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgbNavOutlet } from "@ng-bootstrap/ng-bootstrap"; +import { NgTemplateOutlet } from "@angular/common"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-questionnaires", - templateUrl: "./questionnaires.component.html" + selector: "src-questionnaires", + templateUrl: "./questionnaires.component.html", + standalone: true, + imports: [NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgTemplateOutlet, NgbNavOutlet, MainComponent, QuestionsComponent, TranslatorPipe, TranslateModule] }) export class QuestionnairesComponent implements AfterViewInit { + protected node = inject(NodeResolver); + private cdr = inject(ChangeDetectorRef); + @ViewChild("tab1") tab1!: TemplateRef; @ViewChild("tab2") tab2!: TemplateRef; tabs: Tab[]; nodeData: NodeResolver; active: string; - constructor(protected node: NodeResolver, private cdr: ChangeDetectorRef) { - } - ngAfterViewInit(): void { setTimeout(() => { this.active = "Questionnaires"; diff --git a/client/app/src/pages/admin/questionnaires/questionnaires.module.ts b/client/app/src/pages/admin/questionnaires/questionnaires.module.ts deleted file mode 100644 index 0ac47cf2ff..0000000000 --- a/client/app/src/pages/admin/questionnaires/questionnaires.module.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; - -import {QuestionnairesRoutingModule} from "@app/pages/admin/questionnaires/questionnaires-routing.module"; -import {QuestionnairesComponent} from "@app/pages/admin/questionnaires/questionnaires.component"; -import { - AddFieldFromTemplateComponent -} from "@app/pages/admin/questionnaires/add-field-from-template/add-field-from-template.component"; -import {AddFieldComponent} from "@app/pages/admin/questionnaires/add-field/add-field.component"; -import {FieldsComponent} from "@app/pages/admin/questionnaires/fields/fields.component"; -import {MainComponent} from "@app/pages/admin/questionnaires/main/main.component"; -import {QuestionsComponent} from "@app/pages/admin/questionnaires/questions/questions.component"; -import {StepComponent} from "@app/pages/admin/questionnaires/step/step.component"; -import {StepsComponent} from "@app/pages/admin/questionnaires/steps/steps.component"; -import {FormsModule, ReactiveFormsModule} from "@angular/forms"; -import {RouterModule} from "@angular/router"; -import { - NgbNavModule, - NgbModule, - NgbDropdownModule, - NgbDatepickerModule, - NgbDatepicker -} from "@ng-bootstrap/ng-bootstrap"; -import {NgSelectModule} from "@ng-select/ng-select"; -import {SharedModule} from "@app/shared.module"; -import {StepsListComponent} from "@app/pages/admin/questionnaires/steps-list/steps-list.component"; -import { - QuestionnairesListComponent -} from "@app/pages/admin/questionnaires/questionnaires-list/questionnaires-list.component"; -import {TranslateModule} from "@ngx-translate/core"; -import {MarkdownModule} from "ngx-markdown"; - - -@NgModule({ - declarations: [ - QuestionnairesComponent, - AddFieldFromTemplateComponent, - AddFieldComponent, - FieldsComponent, - MainComponent, - QuestionsComponent, - StepComponent, - StepsComponent, - StepsListComponent, - QuestionnairesListComponent - ], - imports: [ - CommonModule, - QuestionnairesRoutingModule, SharedModule, NgbNavModule, NgbModule, RouterModule, FormsModule, NgSelectModule, - NgbDropdownModule, - TranslateModule, - MarkdownModule, - ReactiveFormsModule, NgbDatepickerModule, NgbDatepicker - - ] -}) -export class QuestionnairesModule { -} \ No newline at end of file diff --git a/client/app/src/pages/admin/questionnaires/questions/questions.component.html b/client/app/src/pages/admin/questionnaires/questions/questions.component.html index 89b18937f7..40a791b3eb 100644 --- a/client/app/src/pages/admin/questionnaires/questions/questions.component.html +++ b/client/app/src/pages/admin/questionnaires/questions/questions.component.html @@ -1,29 +1,33 @@
-
-
- {{ 'Question templates' | translate }} - - - - - -
+
+
+ {{ 'Question templates' | translate }} + + + + +
-
- - - +
+ @if (showAddQuestion) { +
+ @if (questionnairesData.length > 0) { + + }
-
-
-
- -
-
+ } + @for (field of fields | orderBy:'label' ; track field; let index = $index) { +
+
+
+ +
+
+ }
\ No newline at end of file diff --git a/client/app/src/pages/admin/questionnaires/questions/questions.component.ts b/client/app/src/pages/admin/questionnaires/questions/questions.component.ts index c89d12a0d7..89bdc526bd 100644 --- a/client/app/src/pages/admin/questionnaires/questions/questions.component.ts +++ b/client/app/src/pages/admin/questionnaires/questions/questions.component.ts @@ -1,5 +1,5 @@ import {HttpClient} from "@angular/common/http"; -import {Component, ElementRef, OnDestroy, OnInit, ViewChild} from "@angular/core"; +import { Component, ElementRef, OnDestroy, OnInit, ViewChild, inject } from "@angular/core"; import {FieldTemplatesResolver} from "@app/shared/resolvers/field-templates-resolver.service"; import {QuestionnairesResolver} from "@app/shared/resolvers/questionnaires.resolver"; import {HttpService} from "@app/shared/services/http.service"; @@ -9,11 +9,27 @@ import {Subject, takeUntil} from "rxjs"; import {fieldtemplatesResolverModel} from "@app/models/resolvers/field-template-model"; import {Step, questionnaireResolverModel} from "@app/models/resolvers/questionnaire-model"; +import { AddFieldComponent } from "../add-field/add-field.component"; +import { FormsModule } from "@angular/forms"; +import { FieldsComponent } from "../fields/fields.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { OrderByPipe } from "@app/shared/pipes/order-by.pipe"; +import { TranslateModule } from "@ngx-translate/core"; + @Component({ - selector: "src-questions", - templateUrl: "./questions.component.html" + selector: "src-questions", + templateUrl: "./questions.component.html", + standalone: true, + imports: [AddFieldComponent, FormsModule, FieldsComponent, TranslatorPipe, OrderByPipe, TranslateModule] }) export class QuestionsComponent implements OnInit, OnDestroy { + private questionnaireService = inject(QuestionnaireService); + private httpClient = inject(HttpClient); + private httpService = inject(HttpService); + private utilsService = inject(UtilsService); + private fieldTemplates = inject(FieldTemplatesResolver); + private questionnairesResolver = inject(QuestionnairesResolver); + showAddQuestion: boolean = false; fields: fieldtemplatesResolverModel[]; questionnairesData: questionnaireResolverModel[] = []; @@ -22,9 +38,6 @@ export class QuestionsComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); - constructor(private questionnaireService: QuestionnaireService, private httpClient: HttpClient, private httpService: HttpService, private utilsService: UtilsService, private fieldTemplates: FieldTemplatesResolver, private questionnairesResolver: QuestionnairesResolver) { - } - ngOnInit(): void { this.questionnaireService.sharedData = "template"; this.questionnaireService.getQuestionnairesData().pipe(takeUntil(this.destroy$)).subscribe(() => { diff --git a/client/app/src/pages/admin/questionnaires/step/step.component.html b/client/app/src/pages/admin/questionnaires/step/step.component.html index 6e24750616..d1858c781d 100644 --- a/client/app/src/pages/admin/questionnaires/step/step.component.html +++ b/client/app/src/pages/admin/questionnaires/step/step.component.html @@ -1,36 +1,44 @@
-
- -
- {{ 'Questions' | translate }} - - -
-
+
+ +
+ {{ 'Questions' | translate }} + + @if (fieldTemplatesData.length > 0) { + + } +
+
+
+ @if (showAddQuestion) { +
+
+ +
-
-
- -
-
-
-
- -
+ } + @if (showAddQuestionFromTemplate) { +
+
+ +
-
-
-
-
- -
-
+ } +
+ @for (field of step.children | orderBy:['y', 'x']; track field; let index = $index) { +
+
+
+ +
-
+
+ } +
\ No newline at end of file diff --git a/client/app/src/pages/admin/questionnaires/step/step.component.ts b/client/app/src/pages/admin/questionnaires/step/step.component.ts index f4fd39e900..4a2b816f35 100644 --- a/client/app/src/pages/admin/questionnaires/step/step.component.ts +++ b/client/app/src/pages/admin/questionnaires/step/step.component.ts @@ -1,24 +1,35 @@ -import {ChangeDetectorRef, Component, Input, OnInit} from "@angular/core"; +import { ChangeDetectorRef, Component, Input, OnInit, inject } from "@angular/core"; import {ParsedFields} from "@app/models/component-model/parsedFields"; import {fieldtemplatesResolverModel} from "@app/models/resolvers/field-template-model"; import {Step, questionnaireResolverModel} from "@app/models/resolvers/questionnaire-model"; import {FieldTemplatesResolver} from "@app/shared/resolvers/field-templates-resolver.service"; import {HttpService} from "@app/shared/services/http.service"; +import { AddFieldComponent } from "../add-field/add-field.component"; +import { AddFieldFromTemplateComponent } from "../add-field-from-template/add-field-from-template.component"; +import { FormsModule } from "@angular/forms"; +import { FieldsComponent } from "../fields/fields.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { OrderByPipe } from "@app/shared/pipes/order-by.pipe"; +import { TranslateModule } from "@ngx-translate/core"; + @Component({ - selector: "src-step", - templateUrl: "./step.component.html" + selector: "src-step", + templateUrl: "./step.component.html", + standalone: true, + imports: [AddFieldComponent, AddFieldFromTemplateComponent, FormsModule, FieldsComponent, TranslatorPipe, OrderByPipe, TranslateModule] }) export class StepComponent implements OnInit { + private cdr = inject(ChangeDetectorRef); + private httpService = inject(HttpService); + protected fieldTemplates = inject(FieldTemplatesResolver); + @Input() step: Step; @Input() parsedFields: ParsedFields; showAddQuestion: boolean = false; showAddQuestionFromTemplate: boolean = false; fieldTemplatesData: fieldtemplatesResolverModel[] = []; - constructor(private cdr: ChangeDetectorRef, private httpService: HttpService, protected fieldTemplates: FieldTemplatesResolver) { - } - ngOnInit(): void { if (Array.isArray(this.fieldTemplates.dataModel)) { this.fieldTemplatesData = this.fieldTemplates.dataModel; diff --git a/client/app/src/pages/admin/questionnaires/steps-list/steps-list.component.html b/client/app/src/pages/admin/questionnaires/steps-list/steps-list.component.html index 2aeb5e2a95..def6e589cb 100644 --- a/client/app/src/pages/admin/questionnaires/steps-list/steps-list.component.html +++ b/client/app/src/pages/admin/questionnaires/steps-list/steps-list.component.html @@ -1,97 +1,125 @@
-
- - {{index + 1}} - {{step.label}} - - - - - - - - - - - -
-
-
-
-
- - -
+
+ + {{index + 1}} + {{step.label}} + + + + @if (index !== 0) { + + } + @if (index !== questionnaire.steps.length - 1) { + + } + @if (!editing) { + + } + @if (editing) { + + } + @if (editing) { + + } + + + +
+ @if (editing) { +
+
+
+
+ +
-
-
- -
- -
-
+
+
+
+ +
+ +
-
-
- - + + + + +
+ } +
+ + @if (showAddTrigger) { +
+
+ -
-
- -
-
- -
-
- -
-
- - -
-
- -
+
+ @if (new_trigger.field) { +
+
-
- - {{ parsedFields.fields_by_id[trigger.field].label }}: {{ parsedFields.options_by_id[trigger.option].label }} - ({{ 'Sufficient' | translate }}) + } + @if (new_trigger.field) { +
+ +
+ } +
+ +
+
+ } + @for (trigger of step.triggered_by_options; track trigger) { +
+ + {{ parsedFields.fields_by_id[trigger.field].label }}: {{ parsedFields.options_by_id[trigger.option].label }} + @if (trigger.sufficient) { + ({{ 'Sufficient' | translate }}) + }
+ }
- +
+
+ }
\ No newline at end of file diff --git a/client/app/src/pages/admin/questionnaires/steps-list/steps-list.component.ts b/client/app/src/pages/admin/questionnaires/steps-list/steps-list.component.ts index 7efe76eaa9..3cef01124f 100644 --- a/client/app/src/pages/admin/questionnaires/steps-list/steps-list.component.ts +++ b/client/app/src/pages/admin/questionnaires/steps-list/steps-list.component.ts @@ -1,4 +1,4 @@ -import {Component, Input, OnInit} from "@angular/core"; +import { Component, Input, OnInit, inject } from "@angular/core"; import {DeleteConfirmationComponent} from "@app/shared/modals/delete-confirmation/delete-confirmation.component"; import {UtilsService} from "@app/shared/services/utils.service"; import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; @@ -11,11 +11,25 @@ import {Step, questionnaireResolverModel} from "@app/models/resolvers/questionna import {ParsedFields} from "@app/models/component-model/parsedFields"; import {TriggeredByOption} from "@app/models/app/shared-public-model"; +import { FormsModule } from "@angular/forms"; +import { StepComponent } from "../step/step.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; + @Component({ - selector: "src-steps-list", - templateUrl: "./steps-list.component.html" + selector: "src-steps-list", + templateUrl: "./steps-list.component.html", + standalone: true, + imports: [FormsModule, StepComponent, TranslatorPipe, TranslateModule] }) export class StepsListComponent implements OnInit { + private utilsService = inject(UtilsService); + private questionnaireService = inject(QuestionnaireService); + private modalService = inject(NgbModal); + private fieldUtilities = inject(FieldUtilitiesService); + protected nodeResolver = inject(NodeResolver); + private httpService = inject(HttpService); + @Input() step: Step; @Input() steps: Step[]; @Input() questionnaire: questionnaireResolverModel; @@ -29,9 +43,6 @@ export class StepsListComponent implements OnInit { sufficient: true, }; - constructor(private utilsService: UtilsService, private questionnaireService: QuestionnaireService, private modalService: NgbModal, private fieldUtilities: FieldUtilitiesService, protected nodeResolver: NodeResolver, private httpService: HttpService) { - } - ngOnInit(): void { this.parsedFields = this.fieldUtilities.parseQuestionnaire(this.questionnaire, { fields: [], diff --git a/client/app/src/pages/admin/questionnaires/steps/steps.component.html b/client/app/src/pages/admin/questionnaires/steps/steps.component.html index 7e6530eb32..605a95d298 100644 --- a/client/app/src/pages/admin/questionnaires/steps/steps.component.html +++ b/client/app/src/pages/admin/questionnaires/steps/steps.component.html @@ -1,43 +1,49 @@
-
- - {{ 'Steps' | translate }} - - -
-
-
-
-
-
-
- - -
- {{ 'This field is mandatory' | translate }} -
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
- -
+
+ + {{ 'Steps' | translate }} + + +
+ @if (showAddStep) { +
+
+
+
+
+
+ + + @if (newStepForm.form.get('label')?.errors?.['required']) { +
+ {{ 'This field is mandatory' | translate }} +
+ } +
+
+
+
+
+
+ +
+
+
+
+
+ } +
+ @for (stepData of questionnaire.steps; track stepData; let index = $index) { +
+ +
+ } +
\ No newline at end of file diff --git a/client/app/src/pages/admin/questionnaires/steps/steps.component.ts b/client/app/src/pages/admin/questionnaires/steps/steps.component.ts index 61ec5abb53..aa9ad59f7d 100644 --- a/client/app/src/pages/admin/questionnaires/steps/steps.component.ts +++ b/client/app/src/pages/admin/questionnaires/steps/steps.component.ts @@ -1,4 +1,4 @@ -import {Component, Input, OnInit} from "@angular/core"; +import { Component, Input, OnInit, inject } from "@angular/core"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {HttpService} from "@app/shared/services/http.service"; import {UtilsService} from "@app/shared/services/utils.service"; @@ -6,20 +6,29 @@ import {NewStep} from "@app/models/admin/new-step"; import {QuestionnaireService} from "@app/pages/admin/questionnaires/questionnaire.service"; import {Step, questionnaireResolverModel} from "@app/models/resolvers/questionnaire-model"; +import { FormsModule } from "@angular/forms"; +import { StepsListComponent } from "../steps-list/steps-list.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; + @Component({ - selector: "src-steps", - templateUrl: "./steps.component.html" + selector: "src-steps", + templateUrl: "./steps.component.html", + standalone: true, + imports: [FormsModule, StepsListComponent, TranslatorPipe, TranslateModule] }) export class StepsComponent implements OnInit { + private questionnaireService = inject(QuestionnaireService); + protected node = inject(NodeResolver); + protected utilsService = inject(UtilsService); + private httpService = inject(HttpService); + @Input() questionnaire: questionnaireResolverModel; showAddStep: boolean = false; step: Step; editing: boolean = false; new_step: { label: string } = {label: ""}; - constructor(private questionnaireService: QuestionnaireService, protected node: NodeResolver, protected utilsService: UtilsService, private httpService: HttpService) { - } - ngOnInit(): void { this.step = this.questionnaire.steps[0]; } diff --git a/client/app/src/pages/admin/settings/settings-routing.module.ts b/client/app/src/pages/admin/settings/settings-routing.module.ts deleted file mode 100644 index f64fd16fc3..0000000000 --- a/client/app/src/pages/admin/settings/settings-routing.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {SettingsComponent} from "@app/pages/admin/settings/settings.component"; - -const routes: Routes = [ - { - path: "", - component: SettingsComponent, - pathMatch: "full", - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class SettingsRoutingModule { -} \ No newline at end of file diff --git a/client/app/src/pages/admin/settings/settings.component.html b/client/app/src/pages/admin/settings/settings.component.html index 3b40a1b514..a17cb410bc 100644 --- a/client/app/src/pages/admin/settings/settings.component.html +++ b/client/app/src/pages/admin/settings/settings.component.html @@ -1,28 +1,28 @@
- -
- - - - - - - - - - - - - - - + +
+ + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/client/app/src/pages/admin/settings/settings.component.ts b/client/app/src/pages/admin/settings/settings.component.ts index 68ccc0315c..a0302cd92a 100644 --- a/client/app/src/pages/admin/settings/settings.component.ts +++ b/client/app/src/pages/admin/settings/settings.component.ts @@ -1,4 +1,4 @@ -import {Component, TemplateRef, ViewChild, AfterViewInit, ChangeDetectorRef} from "@angular/core"; +import { Component, TemplateRef, ViewChild, AfterViewInit, ChangeDetectorRef, inject } from "@angular/core"; import {NodeResolver} from "app/src/shared/resolvers/node.resolver"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {Tab} from "@app/models/component-model/tab"; @@ -7,12 +7,23 @@ import {Tab2Component} from "@app/pages/admin/settings/tab2/tab2.component"; import {Tab3Component} from "@app/pages/admin/settings/tab3/tab3.component"; import {Tab4Component} from "@app/pages/admin/settings/tab4/tab4.component"; import {Tab5Component} from "@app/pages/admin/settings/tab5/tab5.component"; +import { FormsModule } from "@angular/forms"; +import { NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgbNavOutlet } from "@ng-bootstrap/ng-bootstrap"; +import { NgTemplateOutlet } from "@angular/common"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-admin-settings", - templateUrl: "./settings.component.html" + selector: "src-admin-settings", + templateUrl: "./settings.component.html", + standalone: true, + imports: [FormsModule, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgTemplateOutlet, NgbNavOutlet, Tab1Component, Tab2Component, Tab3Component, Tab4Component, Tab5Component, TranslatorPipe, TranslateModule] }) -export class SettingsComponent implements AfterViewInit { +export class AdminSettingsComponent implements AfterViewInit { + protected node = inject(NodeResolver); + protected authenticationService = inject(AuthenticationService); + private cdr = inject(ChangeDetectorRef); + @ViewChild("tab1") tab1!: TemplateRef; @ViewChild("tab2") tab2!: TemplateRef; @ViewChild("tab3") tab3!: TemplateRef; @@ -22,10 +33,6 @@ export class SettingsComponent implements AfterViewInit { nodeData: NodeResolver; active: string; - constructor(protected node: NodeResolver, protected authenticationService: AuthenticationService, private cdr: ChangeDetectorRef) { - - } - ngAfterViewInit(): void { setTimeout(() => { this.active = "Settings"; diff --git a/client/app/src/pages/admin/settings/settings.module.ts b/client/app/src/pages/admin/settings/settings.module.ts deleted file mode 100644 index d143b82060..0000000000 --- a/client/app/src/pages/admin/settings/settings.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {SettingsRoutingModule} from "@app/pages/admin/settings/settings-routing.module"; -import {SettingsComponent} from "@app/pages/admin/settings/settings.component"; -import {RouterModule} from "@angular/router"; -import {NgbNavModule, NgbModule} from "@ng-bootstrap/ng-bootstrap"; -import {SharedModule} from "@app/shared.module"; -import {Tab2Component} from "@app/pages/admin/settings/tab2/tab2.component"; -import {Tab3Component} from "@app/pages/admin/settings/tab3/tab3.component"; -import {Tab4Component} from "@app/pages/admin/settings/tab4/tab4.component"; -import {Tab5Component} from "@app/pages/admin/settings/tab5/tab5.component"; -import {FormsModule} from "@angular/forms"; -import {NgSelectModule} from "@ng-select/ng-select"; -import {TranslateModule} from "@ngx-translate/core"; - -@NgModule({ - declarations: [ - SettingsComponent, - Tab2Component, - Tab3Component, - Tab4Component, - Tab5Component - ], - imports: [ - CommonModule, - SettingsRoutingModule, SharedModule, NgbNavModule, NgbModule, RouterModule, FormsModule, NgSelectModule, TranslateModule - ] -}) -export class SettingsModule { -} \ No newline at end of file diff --git a/client/app/src/pages/admin/settings/tab1/tab1.component.html b/client/app/src/pages/admin/settings/tab1/tab1.component.html index 28b7214b92..b95046a889 100644 --- a/client/app/src/pages/admin/settings/tab1/tab1.component.html +++ b/client/app/src/pages/admin/settings/tab1/tab1.component.html @@ -1,66 +1,74 @@
-
-
- -
-
-
- - -
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- - -
-
- -
- -
-
-
- - -
-
- - +
+
+ +
+
+
+ + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ @if (authenticationService.session?.role === 'admin') { +
+ +
+
-
- -
- -
+
+ } + @if (authenticationService.session?.role === 'admin') { +
+ + +
+ } + @if (nodeResolver.dataModel.root_tenant || ['default', 'demo'].indexOf(appDataService.public.node.mode) !== -1) { +
+ +
+
-
- +
+ } +
+ + +
+
+ + +
+ @if (nodeResolver.dataModel.root_tenant || ['default', 'demo'].indexOf(appDataService.public.node.mode) !== -1) { +
+ +
+
+
+ } +
+
+
diff --git a/client/app/src/pages/admin/settings/tab1/tab1.component.ts b/client/app/src/pages/admin/settings/tab1/tab1.component.ts index 93ba9f5187..2c627ff268 100644 --- a/client/app/src/pages/admin/settings/tab1/tab1.component.ts +++ b/client/app/src/pages/admin/settings/tab1/tab1.component.ts @@ -1,24 +1,39 @@ -import {Component, Input} from "@angular/core"; -import {ControlContainer, NgForm} from "@angular/forms"; +import { Component, Input, inject } from "@angular/core"; +import { ControlContainer, NgForm, FormsModule } from "@angular/forms"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {UtilsService} from "@app/shared/services/utils.service"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {AppConfigService} from "@app/services/root/app-config.service"; import {Constants} from "@app/shared/constants/constants"; import {AppDataService} from "@app/app-data.service"; +import { ImageUploadDirective } from "../../../../shared/directive/image-upload.directive"; +import { NgClass } from "@angular/common"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-tab1", - templateUrl: "./tab1.component.html", - viewProviders: [{provide: ControlContainer, useExisting: NgForm}], + selector: "src-tab1", + templateUrl: "./tab1.component.html", + viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], + standalone: true, + imports: [ + ImageUploadDirective, + FormsModule, + NgClass, + TranslateModule, + TranslatorPipe +], }) export class Tab1Component { + private appConfigService = inject(AppConfigService); + protected nodeResolver = inject(NodeResolver); + protected appDataService = inject(AppDataService); + protected authenticationService = inject(AuthenticationService); + private utilsService = inject(UtilsService); + protected readonly Constants = Constants; @Input() contentForm: NgForm; - constructor(private appConfigService: AppConfigService, protected nodeResolver: NodeResolver,protected appDataService:AppDataService, protected authenticationService: AuthenticationService, private utilsService: UtilsService) { - } - updateNode() { this.utilsService.update(this.nodeResolver.dataModel).subscribe(_ => { this.appConfigService.reinit(false); diff --git a/client/app/src/pages/admin/settings/tab2/tab2.component.html b/client/app/src/pages/admin/settings/tab2/tab2.component.html index 37a1c46ac3..f102b9f6c6 100644 --- a/client/app/src/pages/admin/settings/tab2/tab2.component.html +++ b/client/app/src/pages/admin/settings/tab2/tab2.component.html @@ -1,47 +1,51 @@ -
+@if (authenticationData.session) { +
-
-
- - - -
-
-
{{ 'Files' | translate }}
-
- - - - -
-
-
- - - - - - - -
{{file.name}} - - - {{ 'Download' | translate }} - - -
-
+
+
+ @for (admin_file of admin_files; track admin_file) { + + }
+
+
{{ 'Files' | translate }}
+
+ + + + +
+
+
+ + + @for (file of files | orderBy:'name'; track file) { + + + + + } + +
{{file.name}} + + + {{ 'Download' | translate }} + + +
+
+
-
- -
+
+ +
-
\ No newline at end of file +
+} \ No newline at end of file diff --git a/client/app/src/pages/admin/settings/tab2/tab2.component.ts b/client/app/src/pages/admin/settings/tab2/tab2.component.ts index 67e3446dd9..8c0db21e17 100644 --- a/client/app/src/pages/admin/settings/tab2/tab2.component.ts +++ b/client/app/src/pages/admin/settings/tab2/tab2.component.ts @@ -1,4 +1,4 @@ -import {Component, ElementRef, Input, OnInit, ViewChild} from "@angular/core"; +import { Component, ElementRef, Input, OnInit, ViewChild, inject } from "@angular/core"; import {NgForm} from "@angular/forms"; import * as Flow from "@flowjs/flow.js"; import type {FlowFile} from "@flowjs/flow.js"; @@ -10,12 +10,26 @@ import {UtilsService} from "@app/shared/services/utils.service"; import {AppConfigService} from "@app/services/root/app-config.service"; import {preferenceResolverModel} from "@app/models/resolvers/preference-resolver-model"; import {AdminFile} from "@app/models/component-model/admin-file"; +import { NgClass } from "@angular/common"; +import { AdminFileComponent } from "../../../../shared/partials/admin-file/admin-file.component"; +import { SwitchComponent } from "../../../../shared/components/switch/switch.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { OrderByPipe } from "@app/shared/pipes/order-by.pipe"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-tab2", - templateUrl: "./tab2.component.html" + selector: "src-tab2", + templateUrl: "./tab2.component.html", + standalone: true, + imports: [NgClass, AdminFileComponent, SwitchComponent, TranslatorPipe, OrderByPipe, TranslateModule] }) export class Tab2Component implements OnInit { + private appConfigService = inject(AppConfigService); + private preferenceResolver = inject(PreferenceResolver); + private utilsService = inject(UtilsService); + private nodeResolver = inject(NodeResolver); + private authenticationService = inject(AuthenticationService); + @Input() contentForm: NgForm; @ViewChild("flowAdvanced", {static: true}) flowAdvanced: FlowDirective; @ViewChild("uploader") uploaderInput: ElementRef; @@ -47,9 +61,6 @@ export class Tab2Component implements OnInit { } ]; - constructor(private appConfigService: AppConfigService, private preferenceResolver: PreferenceResolver, private utilsService: UtilsService, private nodeResolver: NodeResolver, private authenticationService: AuthenticationService) { - } - ngOnInit(): void { this.preferenceData = this.preferenceResolver.dataModel; this.authenticationData = this.authenticationService; diff --git a/client/app/src/pages/admin/settings/tab3/tab3.component.html b/client/app/src/pages/admin/settings/tab3/tab3.component.html index 63ca55ff5a..d2a19234a4 100644 --- a/client/app/src/pages/admin/settings/tab3/tab3.component.html +++ b/client/app/src/pages/admin/settings/tab3/tab3.component.html @@ -1,50 +1,62 @@
-
- - -
- - -
-
-
-
- - - - [{{ item.code }}] - - -
-
-
-
    -
  • - - - - - - - - - - - - - -
  • -
+
+ + +
+ + +
+
+ @if (showLangSelect) { +
+
+ + + + [{{ item.code }}] + +
-
-
- -
+
+ } +
+
    + @for (lang_code of nodeResolver.dataModel.languages_enabled; track lang_code; let index = $index) { +
  • + + @if (lang_code === nodeResolver.dataModel.default_language) { + + + + } + @if (lang_code !== nodeResolver.dataModel.default_language) { + + + + } + @if (lang_code !== nodeResolver.dataModel.default_language) { + + + + } + @if (lang_code === nodeResolver.dataModel.default_language) { + + } + + +
  • + } +
+
+
+ +
+
diff --git a/client/app/src/pages/admin/settings/tab3/tab3.component.ts b/client/app/src/pages/admin/settings/tab3/tab3.component.ts index 19e0bbd052..8e72dd7b60 100644 --- a/client/app/src/pages/admin/settings/tab3/tab3.component.ts +++ b/client/app/src/pages/admin/settings/tab3/tab3.component.ts @@ -1,5 +1,5 @@ -import {Component, Input, OnInit} from "@angular/core"; -import {NgForm} from "@angular/forms"; +import { Component, Input, OnInit, inject } from "@angular/core"; +import { NgForm, FormsModule } from "@angular/forms"; import {LanguageUtils} from "@app/pages/admin/settings/helper-methods/language-utils"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {UtilsService} from "@app/shared/services/utils.service"; @@ -8,11 +8,24 @@ import {TranslationService} from "@app/services/helper/translation.service"; import {AppDataService} from "@app/app-data.service"; import {LanguagesSupported} from "@app/models/app/public-model"; +import { NgSelectComponent, NgOptionTemplateDirective } from "@ng-select/ng-select"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { FilterPipe } from "@app/shared/pipes/filter.pipe"; +import { TranslateModule } from "@ngx-translate/core"; + @Component({ - selector: "src-tab3", - templateUrl: "./tab3.component.html" + selector: "src-tab3", + templateUrl: "./tab3.component.html", + standalone: true, + imports: [FormsModule, NgSelectComponent, NgOptionTemplateDirective, TranslatorPipe, FilterPipe, TranslateModule] }) export class Tab3Component implements OnInit { + private appDataService = inject(AppDataService); + private translationService = inject(TranslationService); + private appConfigService = inject(AppConfigService); + private utilsService = inject(UtilsService); + protected nodeResolver = inject(NodeResolver); + @Input() contentForm: NgForm; showLangSelect = false; @@ -20,9 +33,6 @@ export class Tab3Component implements OnInit { languageUtils: LanguageUtils languagesNotEnabled: LanguagesSupported[]; - constructor(private appDataService: AppDataService, private translationService: TranslationService, private appConfigService: AppConfigService, private utilsService: UtilsService, protected nodeResolver: NodeResolver) { - } - ngOnInit(): void { this.updateLanguages(); } diff --git a/client/app/src/pages/admin/settings/tab4/tab4.component.html b/client/app/src/pages/admin/settings/tab4/tab4.component.html index b597f7635c..09ada1eb24 100644 --- a/client/app/src/pages/admin/settings/tab4/tab4.component.html +++ b/client/app/src/pages/admin/settings/tab4/tab4.component.html @@ -1,59 +1,69 @@
-
-
- - -
-
- - -

-
- -
- -
-
-
-
- -
+
+
+ + +
+
+ + +

+ @if (vars.text_to_customize) { +
+ +
+ +
+
+
+ } +
+
+
-
-
-
-
-
+
+
+
+ @if (custom_texts) { +
+
- - - - - - - - - - - - - +
{{'Original text' | translate}}{{'Original translation' | translate}}{{'Custom translation' | translate}}
{{ item.key }}{{ default_texts[item.key] }} - {{ custom_texts[item.key] }} - -
+ + + + + + + + + @for (item of customTextsKeys(); track item) { + + + + - -
{{'Original text' | translate}}{{'Original translation' | translate}}{{'Custom translation' | translate}}
{{ item.key }}{{ default_texts[item.key] }} + {{ custom_texts[item.key] }} + +
+ } + +
+
-
\ No newline at end of file + } \ No newline at end of file diff --git a/client/app/src/pages/admin/settings/tab4/tab4.component.ts b/client/app/src/pages/admin/settings/tab4/tab4.component.ts index 1c6c7bce8a..42e0f9f675 100644 --- a/client/app/src/pages/admin/settings/tab4/tab4.component.ts +++ b/client/app/src/pages/admin/settings/tab4/tab4.component.ts @@ -1,14 +1,22 @@ -import {Component, Input, OnInit} from "@angular/core"; -import {NgForm} from "@angular/forms"; +import { Component, Input, OnInit, inject } from "@angular/core"; +import { NgForm, FormsModule } from "@angular/forms"; import {LanguageUtils} from "@app/pages/admin/settings/helper-methods/language-utils"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {UtilsService} from "@app/shared/services/utils.service"; +import { NgClass } from "@angular/common"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-tab4", - templateUrl: "./tab4.component.html" + selector: "src-tab4", + templateUrl: "./tab4.component.html", + standalone: true, + imports: [FormsModule, NgClass, TranslatorPipe, TranslateModule] }) export class Tab4Component implements OnInit { + protected utilsService = inject(UtilsService); + protected nodeResolver = inject(NodeResolver); + @Input() contentForm: NgForm; vars: { language_to_customize: string, text_to_customize: string, custom_text: string } = { @@ -22,10 +30,6 @@ export class Tab4Component implements OnInit { customTextsExist: boolean = false; languageUtils: LanguageUtils - - constructor(protected utilsService: UtilsService, protected nodeResolver: NodeResolver) { - } - ngOnInit(): void { this.initLanguages(); } diff --git a/client/app/src/pages/admin/settings/tab5/tab5.component.html b/client/app/src/pages/admin/settings/tab5/tab5.component.html index d8f2730fe4..92a32bb1ae 100644 --- a/client/app/src/pages/admin/settings/tab5/tab5.component.html +++ b/client/app/src/pages/admin/settings/tab5/tab5.component.html @@ -1,185 +1,205 @@
-
-
- -
+
+
+ +
+
+ +
+ @if (nodeResolver.dataModel.encryption) { +
- -
-
-
+ +
+ @if (nodeResolver.dataModel.escrow) { +
+ +
    + @for (user of userData; track user) { +
  1. + {{user.name}} +
  2. + } +
+
+ } +
+ } +
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+ +
+
+ + +
+
+ +
+
+ + +
{{ 'For security reasons, password changes are required at regular intervals.' | translate }}
+
{{ 'Set the value to 0 to disable this feature.' | translate }}
+
+ @if (nodeResolver.dataModel.root_tenant) { +
+ + +
+ } +
+ + +
+
+ +
+ @if (!nodeResolver.dataModel.disable_privacy_badge) { +
+ +
+ } + @if (!nodeResolver.dataModel.disable_privacy_badge && nodeResolver.dataModel.enable_custom_privacy_badge) { +
+ +
+ +
+
+ } +
+ + +
+ @if (nodeResolver.dataModel.root_tenant) { +
+ + +
+ } + @if (nodeResolver.dataModel.root_tenant) { +
+ + + + + + + + + + + + + + + + + + + + + +
{{ 'Low' | translate }}{{ 'High' | translate }}
- -
- -
    -
  1. - {{user.name}} -
  2. -
-
- -
- -
-
- -
-
- -
-
- -
- -
-
-
- -
-
- - -
-
- -
-
- - -
{{ 'For security reasons, password changes are required at regular intervals.' | translate }}
-
{{ 'Set the value to 0 to disable this feature.' | translate }}
-
-
- - -
-
- - -
-
- -
-
- -
-
- -
- -
-
-
- - -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - -
{{ 'Low' | translate }}{{ 'High' | translate }}
- - - - - -
- - - - - -
-
-
- - -
-
- - -
-
- - -
{{ 'By enabling this feature, you will contribute to the development and security of the platform.' | - translate }}
-
-
- - -
+
+ + + +
+ + + + + +
+
+ } +
+ + +
+ @if (nodeResolver.dataModel.root_tenant) { +
+ + +
+ } + @if (nodeResolver.dataModel.root_tenant) { +
+ + +
{{ 'By enabling this feature, you will contribute to the development and security of the platform.' | + translate }}
+
+ } +
+ +
+
diff --git a/client/app/src/pages/admin/settings/tab5/tab5.component.ts b/client/app/src/pages/admin/settings/tab5/tab5.component.ts index f9ccacb5c5..3a73546f83 100644 --- a/client/app/src/pages/admin/settings/tab5/tab5.component.ts +++ b/client/app/src/pages/admin/settings/tab5/tab5.component.ts @@ -1,5 +1,5 @@ -import {Component, Input, OnInit} from "@angular/core"; -import {NgForm} from "@angular/forms"; +import { Component, Input, OnInit, inject } from "@angular/core"; +import { NgForm, FormsModule } from "@angular/forms"; import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; import {Constants} from "@app/shared/constants/constants"; import {EnableEncryptionComponent} from "@app/shared/modals/enable-encryption/enable-encryption.component"; @@ -12,21 +12,31 @@ import {AppConfigService} from "@app/services/root/app-config.service"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {userResolverModel} from "@app/models/resolvers/user-resolver-model"; import {questionnaireResolverModel} from "@app/models/resolvers/questionnaire-model"; +import { NgClass } from "@angular/common"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-tab5", - templateUrl: "./tab5.component.html" + selector: "src-tab5", + templateUrl: "./tab5.component.html", + standalone: true, + imports: [FormsModule, NgClass, TranslatorPipe, TranslateModule] }) export class Tab5Component implements OnInit { + private authenticationService = inject(AuthenticationService); + private modalService = inject(NgbModal); + private appConfigService = inject(AppConfigService); + private utilsService = inject(UtilsService); + protected nodeResolver = inject(NodeResolver); + protected preferenceResolver = inject(PreferenceResolver); + private usersResolver = inject(UsersResolver); + private questionnairesResolver = inject(QuestionnairesResolver); + @Input() contentForm: NgForm; userData: userResolverModel[]; questionnaireData: questionnaireResolverModel[]; routeReload = false; - constructor(private authenticationService: AuthenticationService, private modalService: NgbModal, private appConfigService: AppConfigService, private utilsService: UtilsService, protected nodeResolver: NodeResolver, protected preferenceResolver: PreferenceResolver, private usersResolver: UsersResolver, private questionnairesResolver: QuestionnairesResolver) { - - } - protected readonly Constants = Constants; ngOnInit(): void { diff --git a/client/app/src/pages/admin/sidebar/sidebar.component.html b/client/app/src/pages/admin/sidebar/sidebar.component.html index be64d2ad9c..be6c05b881 100644 --- a/client/app/src/pages/admin/sidebar/sidebar.component.html +++ b/client/app/src/pages/admin/sidebar/sidebar.component.html @@ -1,41 +1,45 @@ - - {{'Home'|translate}} + + {{'Home'|translate}} - - {{'Settings'|translate}} + + {{'Settings'|translate}} - - {{'Users'|translate}} + + {{'Users'|translate}} - - {{'Questionnaires'|translate}} + + {{'Questionnaires'|translate}} - - {{'Channels'|translate}} + + {{'Channels'|translate}} - - {{'Case management'|translate}} + + {{'Case management'|translate}} - - {{'Notifications'|translate}} + + {{'Notifications'|translate}} - +@if (nodeResolver.dataModel.root_tenant || authenticationService.session.properties.management_session) { + {{'Network'|translate}} - - + +} +@if (nodeResolver.dataModel.root_tenant) { + {{'Sites'|translate}} - + +} - - {{'Audit log'|translate}} + + {{'Audit log'|translate}} diff --git a/client/app/src/pages/admin/sidebar/sidebar.component.ts b/client/app/src/pages/admin/sidebar/sidebar.component.ts index 61991c5cb1..6858ace821 100644 --- a/client/app/src/pages/admin/sidebar/sidebar.component.ts +++ b/client/app/src/pages/admin/sidebar/sidebar.component.ts @@ -1,17 +1,22 @@ -import {ChangeDetectionStrategy, Component} from "@angular/core"; -import {Router} from "@angular/router"; +import { ChangeDetectionStrategy, Component, inject } from "@angular/core"; +import { Router, RouterLink, RouterLinkActive } from "@angular/router"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; + @Component({ - selector: "src-admin-sidebar", - templateUrl: "./sidebar.component.html", - changeDetection: ChangeDetectionStrategy.OnPush + selector: "src-admin-sidebar", + templateUrl: "./sidebar.component.html", + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [RouterLink, RouterLinkActive, TranslatorPipe] }) -export class SidebarComponent { +export class AdminSidebarComponent { + private router = inject(Router); + protected nodeResolver = inject(NodeResolver); + protected authenticationService = inject(AuthenticationService); - constructor(private router: Router, protected nodeResolver: NodeResolver, protected authenticationService: AuthenticationService) { - } isActive(route: string): boolean { return this.router.isActive(route, { diff --git a/client/app/src/pages/admin/sites/sites-routing.module.ts b/client/app/src/pages/admin/sites/sites-routing.module.ts deleted file mode 100644 index 4629e2c619..0000000000 --- a/client/app/src/pages/admin/sites/sites-routing.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {SitesComponent} from "@app/pages/admin/sites/sites.component"; - -const routes: Routes = [ - { - path: "", - component: SitesComponent, - pathMatch: "full", - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class SitesRoutingModule { -} \ No newline at end of file diff --git a/client/app/src/pages/admin/sites/sites-tab1/sites-tab1.component.html b/client/app/src/pages/admin/sites/sites-tab1/sites-tab1.component.html index 78e29829ed..ac31e34c92 100644 --- a/client/app/src/pages/admin/sites/sites-tab1/sites-tab1.component.html +++ b/client/app/src/pages/admin/sites/sites-tab1/sites-tab1.component.html @@ -1,61 +1,71 @@
-
-
- {{'Sites'|translate}} - - -
-
- -
+
+
+ {{'Sites'|translate}} + + +
+
+ +
+
-
-
-
-
+ @if (showAddTenant) { +
+
+
- -
- -
+ +
+ +
- - + +
- +
- -
-
-
-
-
-
-
-
- - - - - - +
+ } +
+ @if (tenants) { +
+ @for (tenant of tenants | orderBy:'id' | search:search | slice: (currentPage - 1) * itemsPerPage : currentPage * itemsPerPage; track tenant; let index = $index) { +
+
+ +
+
+ } + @if ((tenants | filter:'name':search).length > 10) { +
+ + + < {{'Previous' | translate}} + {{'Next' | translate}} > + + << {{'First' | translate}} + {{'Last' | translate}} >> + +
+ }
+ }
-
diff --git a/client/app/src/pages/admin/sites/sites-tab1/sites-tab1.component.ts b/client/app/src/pages/admin/sites/sites-tab1/sites-tab1.component.ts index f98e2a776b..b2e1337867 100644 --- a/client/app/src/pages/admin/sites/sites-tab1/sites-tab1.component.ts +++ b/client/app/src/pages/admin/sites/sites-tab1/sites-tab1.component.ts @@ -1,12 +1,25 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {tenantResolverModel} from "@app/models/resolvers/tenant-resolver-model"; import {HttpService} from "@app/shared/services/http.service"; +import { FormsModule } from "@angular/forms"; +import { SlicePipe } from "@angular/common"; +import { SiteslistComponent } from "../siteslist/siteslist.component"; +import { NgbPagination, NgbPaginationPrevious, NgbPaginationNext, NgbPaginationFirst, NgbPaginationLast } from "@ng-bootstrap/ng-bootstrap"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { FilterPipe } from "@app/shared/pipes/filter.pipe"; +import { FilterSearchPipe } from "@app/shared/pipes/filter-search.pipe"; +import { OrderByPipe } from "@app/shared/pipes/order-by.pipe"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-sites-tab1", - templateUrl: "./sites-tab1.component.html" + selector: "src-sites-tab1", + templateUrl: "./sites-tab1.component.html", + standalone: true, + imports: [FormsModule, SiteslistComponent, NgbPagination, NgbPaginationPrevious, NgbPaginationNext, NgbPaginationFirst, NgbPaginationLast, SlicePipe, TranslatorPipe, FilterPipe, FilterSearchPipe, OrderByPipe, TranslateModule] }) export class SitesTab1Component implements OnInit { + private httpService = inject(HttpService); + search: string; newTenant: { name: string, active: boolean, mode: string, subdomain: string } = { name: "", @@ -31,9 +44,6 @@ export class SitesTab1Component implements OnInit { this.showAddTenant = !this.showAddTenant; } - constructor(private httpService: HttpService) { - } - addTenant() { this.httpService.addTenant(this.newTenant).subscribe(res => { this.tenants.push(res); diff --git a/client/app/src/pages/admin/sites/sites-tab2/sites-tab2.component.html b/client/app/src/pages/admin/sites/sites-tab2/sites-tab2.component.html index 0d8acbaf77..c575bf30cb 100644 --- a/client/app/src/pages/admin/sites/sites-tab2/sites-tab2.component.html +++ b/client/app/src/pages/admin/sites/sites-tab2/sites-tab2.component.html @@ -1,90 +1,100 @@
-
-
- -
-
- - -
-
- -
- -
-
+
+
+ +
+
+ + +
+
+ +
+ +
+
+
+ + +
+ @if (!nodeResolver.dataModel.adminonly) { +
- - +
-
+ @if (nodeResolver.dataModel.enable_signup) { +
- -
-
-
- -
-
- - -
-
- -
- -
-
-
- - -
+ + @if (nodeResolver.dataModel.signup_tos1_enable) { +
+
+ + +
+
+ +
+
+
+
+ + +
-
- -
-
- - -
-
- -
- -
-
-
- - -
+ } +
+
+ + @if (nodeResolver.dataModel.signup_tos2_enable) { +
+
+ + +
+
+ +
+
+
+
+ + +
+ }
-
- -
-
+
+ } +
+ } + +
+
\ No newline at end of file diff --git a/client/app/src/pages/admin/sites/sites-tab2/sites-tab2.component.ts b/client/app/src/pages/admin/sites/sites-tab2/sites-tab2.component.ts index e26d356fab..0c97fe63a9 100644 --- a/client/app/src/pages/admin/sites/sites-tab2/sites-tab2.component.ts +++ b/client/app/src/pages/admin/sites/sites-tab2/sites-tab2.component.ts @@ -1,14 +1,20 @@ -import {Component} from "@angular/core"; +import { Component, inject } from "@angular/core"; import {QuestionnairesResolver} from "@app/shared/resolvers/questionnaires.resolver"; import {UtilsService} from "@app/shared/services/utils.service"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; +import { FormsModule } from "@angular/forms"; + +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-sites-tab2", - templateUrl: "./sites-tab2.component.html" + selector: "src-sites-tab2", + templateUrl: "./sites-tab2.component.html", + standalone: true, + imports: [FormsModule, TranslatorPipe, TranslateModule] }) export class SitesTab2Component { - - constructor(protected nodeResolver: NodeResolver, protected utilsService: UtilsService, public questionnairesResolver: QuestionnairesResolver) { - } + protected nodeResolver = inject(NodeResolver); + protected utilsService = inject(UtilsService); + questionnairesResolver = inject(QuestionnairesResolver); } \ No newline at end of file diff --git a/client/app/src/pages/admin/sites/sites.component.html b/client/app/src/pages/admin/sites/sites.component.html index 4999deabcc..adad0d7b05 100644 --- a/client/app/src/pages/admin/sites/sites.component.html +++ b/client/app/src/pages/admin/sites/sites.component.html @@ -1,19 +1,19 @@
- -
- - - - - - + +
+ + + + + +
\ No newline at end of file diff --git a/client/app/src/pages/admin/sites/sites.component.ts b/client/app/src/pages/admin/sites/sites.component.ts index aaeee04b3e..a5ffaeaa50 100644 --- a/client/app/src/pages/admin/sites/sites.component.ts +++ b/client/app/src/pages/admin/sites/sites.component.ts @@ -1,15 +1,26 @@ -import {Component, TemplateRef, ViewChild, OnInit, AfterViewInit, ChangeDetectorRef} from "@angular/core"; +import { Component, TemplateRef, ViewChild, OnInit, AfterViewInit, ChangeDetectorRef, inject } from "@angular/core"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {Tab} from "@app/models/component-model/tab"; import {SitesTab1Component} from "@app/pages/admin/sites/sites-tab1/sites-tab1.component"; import {SitesTab2Component} from "@app/pages/admin/sites/sites-tab2/sites-tab2.component"; +import { FormsModule } from "@angular/forms"; +import { NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgbNavOutlet } from "@ng-bootstrap/ng-bootstrap"; +import { NgTemplateOutlet } from "@angular/common"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-sites", - templateUrl: "./sites.component.html" + selector: "src-sites", + templateUrl: "./sites.component.html", + standalone: true, + imports: [FormsModule, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgTemplateOutlet, NgbNavOutlet, SitesTab1Component, SitesTab2Component, TranslatorPipe, TranslateModule] }) export class SitesComponent implements OnInit, AfterViewInit { + node = inject(NodeResolver); + authenticationService = inject(AuthenticationService); + private cdr = inject(ChangeDetectorRef); + @ViewChild("tab1") tab1!: TemplateRef; @ViewChild("tab2") tab2!: TemplateRef; @@ -17,9 +28,6 @@ export class SitesComponent implements OnInit, AfterViewInit { nodeData: NodeResolver; active: string; - constructor(public node: NodeResolver, public authenticationService: AuthenticationService, private cdr: ChangeDetectorRef) { - } - ngOnInit() { } diff --git a/client/app/src/pages/admin/sites/sites.module.ts b/client/app/src/pages/admin/sites/sites.module.ts deleted file mode 100644 index 2da2adc517..0000000000 --- a/client/app/src/pages/admin/sites/sites.module.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {SitesComponent} from "@app/pages/admin/sites/sites.component"; -import {SitesTab1Component} from "@app/pages/admin/sites/sites-tab1/sites-tab1.component"; -import {SitesTab2Component} from "@app/pages/admin/sites/sites-tab2/sites-tab2.component"; -import {SharedModule} from "@app/shared.module"; -import {NgbModule, NgbNavModule} from "@ng-bootstrap/ng-bootstrap"; -import {RouterModule} from "@angular/router"; -import {FormsModule} from "@angular/forms"; -import {NgSelectModule} from "@ng-select/ng-select"; -import {TranslateModule} from "@ngx-translate/core"; -import {SitesRoutingModule} from "@app/pages/admin/sites/sites-routing.module"; -import {SiteslistComponent} from "@app/pages/admin/sites/siteslist/siteslist.component"; - - -@NgModule({ - declarations: [ - SitesComponent, - SitesTab1Component, - SitesTab2Component, - SiteslistComponent - ], - imports: [ - CommonModule, - SitesRoutingModule, SharedModule, NgbNavModule, NgbModule, RouterModule, FormsModule, NgSelectModule, TranslateModule - ] -}) -export class SitesModule { -} \ No newline at end of file diff --git a/client/app/src/pages/admin/sites/siteslist/siteslist.component.html b/client/app/src/pages/admin/sites/siteslist/siteslist.component.html index e817bb54d1..f015586fce 100644 --- a/client/app/src/pages/admin/sites/siteslist/siteslist.component.html +++ b/client/app/src/pages/admin/sites/siteslist/siteslist.component.html @@ -1,60 +1,86 @@
-
- -
- - - - - - - - -
+
+
+
+ {{tenant.id}} + {{tenant.name}} +
+ @if (tenant.active && (tenant.onionservice || tenant.hostname || (tenant.subdomain || nodeResolver.dataModel.rootdomain))) { +
+ @if (tenant.onionservice) { + {{tenant.onionservice}} + } + @if (tenant.subdomain) { + + @if (nodeResolver.dataModel.onionservice && tenant.subdomain) { + {{ tenant.subdomain }} + .{{ nodeResolver.dataModel.onionservice }} + } + + } + @if (tenant.hostname) { + {{ tenant.hostname }} + }
-
-
-
-
-
- - -
-
- - -
-
- - -
-
- -
-
-
+ } +
+ @if (tenant.id !== 1) { +
+ + @if (tenant.active) { + + } + @if (!tenant.active) { + + } + @if (tenant.active) { + + } + @if (!editing) { + + } + @if (editing) { + + } + @if (isRemovableTenant()) { + + } + +
+ } +
+ @if (editing) { +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ } +
diff --git a/client/app/src/pages/admin/sites/siteslist/siteslist.component.ts b/client/app/src/pages/admin/sites/siteslist/siteslist.component.ts index 0a0fe9adb8..ecd12db4b7 100644 --- a/client/app/src/pages/admin/sites/siteslist/siteslist.component.ts +++ b/client/app/src/pages/admin/sites/siteslist/siteslist.component.ts @@ -1,28 +1,36 @@ -import {Component, Input} from "@angular/core"; +import { Component, Input, inject } from "@angular/core"; import {AppDataService} from "@app/app-data.service"; import {DeleteConfirmationComponent} from "@app/shared/modals/delete-confirmation/delete-confirmation.component"; import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; import {HttpService} from "@app/shared/services/http.service"; import {UtilsService} from "@app/shared/services/utils.service"; -import {NgForm} from "@angular/forms"; +import { NgForm, FormsModule } from "@angular/forms"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {tenantResolverModel} from "@app/models/resolvers/tenant-resolver-model"; import {Observable} from "rxjs"; +import { DatePipe } from "@angular/common"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { TranslateModule } from "@ngx-translate/core"; @Component({ - selector: "src-siteslist", - templateUrl: "./siteslist.component.html" + selector: "src-siteslist", + templateUrl: "./siteslist.component.html", + standalone: true, + imports: [FormsModule, DatePipe, TranslatorPipe, TranslateModule] }) export class SiteslistComponent { + protected nodeResolver = inject(NodeResolver); + protected appDataService = inject(AppDataService); + private modalService = inject(NgbModal); + private httpService = inject(HttpService); + private utilsService = inject(UtilsService); + @Input() editTenant: NgForm; @Input() tenant: tenantResolverModel; @Input() tenants: tenantResolverModel[]; @Input() index: number; editing = false; - constructor(protected nodeResolver: NodeResolver, protected appDataService: AppDataService, private modalService: NgbModal, private httpService: HttpService, private utilsService: UtilsService) { - } - toggleActivation(event: Event): void { event.stopPropagation(); this.tenant.active = !this.tenant.active; diff --git a/client/app/src/pages/admin/users/user-editor/user-editor.component.html b/client/app/src/pages/admin/users/user-editor/user-editor.component.html index 3cc2c7cc09..0cff33ea8c 100644 --- a/client/app/src/pages/admin/users/user-editor/user-editor.component.html +++ b/client/app/src/pages/admin/users/user-editor/user-editor.component.html @@ -1,220 +1,288 @@
-
- - {{ user.name }} - - - {{ 'Admin' | translate }} - {{ 'Recipient' | translate }} - {{ 'Custodian' | translate }} - {{ 'Analyst' | translate }} - - - {{ 'Disabled' | translate }} - - - +
+ + {{ user.name }} + + @switch (user.role) { + @case ('admin') { + {{ 'Admin' | translate }} + } + @case ('receiver') { + {{ 'Recipient' | translate }} + } + @case ('custodian') { + {{ 'Custodian' | translate }} + } + @case ('analyst') { + {{ 'Analyst' | translate }} + } + } + + @if (!user.enabled) { + {{ 'Disabled' | translate }} + } + @if (nodeData.encryption && !user.encryption) { + + - - - - - - - - -
-
-
-
-
+ } + + + + @if (!editing) { + + } + @if (editing) { + + } + @if (editing) { + + } + @if (getUserID() !== user.id) { + + } + + +
+ @if (editing) { +
+
+
+
+
+
+
+
+
+
+
+ @if (user.role !== 'receiver' || !nodeData.simplified_login) { +
+ + + @if (editUser.form.get('username')?.errors?.['required']) { +
{{'This field is mandatory' | translate }}
+ } +
+ } +
+ + + @if (editUser.form.get('name')?.errors?.['required']) { +
This field is mandatory
+ } +
+
+ + +
+
+ + +
+
+ + + @if (editUser.form.get('email')?.errors?.['required']) { +
+ {{ 'This field is mandatory' | translate }} +
+ } + @if (editUser.form.get('email')?.errors?.['pattern']) { +
Invalid email address
+ } +
+ @if (getUserID() !== user.id) { +
+ +
+ + + @if (user.last_login === '1970-01-01T00:00:00Z') { + {{ 'Send activation link' | + translate }} + } + @if (user.last_login !== '1970-01-01T00:00:00Z') { + {{ 'Send reset link' | + translate }} + } + + @if (!user.encryption || preferenceData.escrow) { + + + {{ 'Set password' | translate }} + + } +
+ @if (user.newpassword) {
-
+ + + + + + + + @if (passwordStrengthScore>0) { + + } + @if (passwordStrengthScore && passwordStrengthScore < 2) { +
{{ 'The chosen password is too weak. A valid password should be at least 12 characters long and contain a variety of characters including at least a lowercase character, a capital character, a number and a special character.' | translate }}
+ }
+ } +
+ } +
+ + +
+
+ + + @if (changePasswordArgs.password_change_needed) { +
+ {{ 'Enabled' | translate }} + {{ 'The user will be forced to change its password on next login.' | translate}} +
+ } +
+ @if (user.two_factor) { +
+ + + {{ 'Disable two factor authentication' | translate }} +
+ } +
+ + +
+
+ + +
+
+ @if (nodeData.pgp) {
-
-
- - -
{{'This field is mandatory' | translate }}
-
-
- - -
This field is mandatory
-
+
+ @if (user.pgp_key_fingerprint) { +
- - + +

+ {{ 'Fingerprint' | translate }}: {{ user.pgp_key_fingerprint }} +

+

+ {{ 'Expiration date' | translate }}: {{ user.pgp_key_expiration | + date:"dd/MM/yyyy" }} +

- - + +
-
- - -
- {{ 'This field is mandatory' | translate }} -
-
Invalid email address
-
-
- -
- - - {{ 'Send activation link' | - translate }} - {{ 'Send reset link' | - translate }} - - - - {{ 'Set password' | translate }} - -
-
- - - - - - - - -
{{ 'The chosen password is too weak. A valid password should be at least 12 characters long and contain a variety of characters including at least a lowercase character, a capital character, a number and a special character.' | translate }}
-
-
-
- - -
-
- - -
- {{ 'Enabled' | translate }} - {{ 'The user will be forced to change its password on next login.' | translate}} -
-
-
- - - {{ 'Disable two factor authentication' | translate }} - -
-
- - -
-
- - +
+ } + @if (user.pgp_key_fingerprint === '') { +
+
+ +
+ + +
-
+
+ } +
-
-
-
-
- -

- {{ 'Fingerprint' | translate }}: {{ user.pgp_key_fingerprint }} -

-

- {{ 'Expiration date' | translate }}: {{ user.pgp_key_expiration | - date:"dd/MM/yyyy" }} -

-
-
- - -
-
-
-
- -
- - -
-
-
-
+ } + @if (user.role === 'admin') { +
+
+ @if (nodeData.escrow && user.encryption && preferenceData.escrow) { +
+ + +
+ } +
-
-
-
- - -
+ } + @if (user.role === 'receiver') { +
+
+
+ +
-
-
-
-
- - -
-
- - {{ 'Give this user ability to mask information' | translate }} -
-
- - {{ 'Give this user ability to permanently redact masked information' | translate }} -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
+
+ + {{ 'Give this user ability to mask information' | translate }} +
+
+ + {{ 'Give this user ability to permanently redact masked information' | translate }} +
+
+ +
+ @if (!nodeData.can_postpone_expiration) { +
+ + +
+ } +
+ + +
+
+ + +
+
+ + +
+
-
-
- -
- - {{ 'Settings' | translate }} -
+ } + @if (user.role !== 'admin') { +
+
+ +
+ + {{ 'Settings' | translate }}
+
+ }
+ }
diff --git a/client/app/src/pages/admin/users/user-editor/user-editor.component.ts b/client/app/src/pages/admin/users/user-editor/user-editor.component.ts index 47881528b7..2fa7aac403 100644 --- a/client/app/src/pages/admin/users/user-editor/user-editor.component.ts +++ b/client/app/src/pages/admin/users/user-editor/user-editor.component.ts @@ -1,5 +1,5 @@ -import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from "@angular/core"; -import {NgForm} from "@angular/forms"; +import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild, inject } from "@angular/core"; +import { NgForm, FormsModule } from "@angular/forms"; import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; import {AppDataService} from "@app/app-data.service"; import {AuthenticationService} from "@app/services/helper/authentication.service"; @@ -12,12 +12,26 @@ import {Observable} from "rxjs"; import {userResolverModel} from "@app/models/resolvers/user-resolver-model"; import {nodeResolverModel} from "@app/models/resolvers/node-resolver-model"; import {preferenceResolverModel} from "@app/models/resolvers/preference-resolver-model"; +import { NgClass, DatePipe } from "@angular/common"; +import { ImageUploadDirective } from "../../../../shared/directive/image-upload.directive"; +import { PasswordStrengthValidatorDirective } from "../../../../shared/directive/password-strength-validator.directive"; +import { PasswordMeterComponent } from "../../../../shared/components/password-meter/password-meter.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-user-editor", - templateUrl: "./user-editor.component.html" + selector: "src-user-editor", + templateUrl: "./user-editor.component.html", + standalone: true, + imports: [ImageUploadDirective, FormsModule, PasswordStrengthValidatorDirective, NgClass, PasswordMeterComponent, DatePipe, TranslatorPipe] }) export class UserEditorComponent implements OnInit { + private modalService = inject(NgbModal); + private appDataService = inject(AppDataService); + private preference = inject(PreferenceResolver); + private authenticationService = inject(AuthenticationService); + private nodeResolver = inject(NodeResolver); + private utilsService = inject(UtilsService); + @Input() user: userResolverModel; @Input() users: userResolverModel[]; @Input() index: number; @@ -34,9 +48,6 @@ export class UserEditorComponent implements OnInit { appServiceData: AppDataService; protected readonly Constants = Constants; - constructor(private modalService: NgbModal, private appDataService: AppDataService, private preference: PreferenceResolver, private authenticationService: AuthenticationService, private nodeResolver: NodeResolver, private utilsService: UtilsService) { - } - ngOnInit(): void { if (this.nodeResolver.dataModel) { this.nodeData = this.nodeResolver.dataModel; diff --git a/client/app/src/pages/admin/users/users-routing.module.ts b/client/app/src/pages/admin/users/users-routing.module.ts deleted file mode 100644 index 2244a9a002..0000000000 --- a/client/app/src/pages/admin/users/users-routing.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {UsersComponent} from "@app/pages/admin/users/users.component"; - -const routes: Routes = [ - { - path: "", - component: UsersComponent, - pathMatch: "full", - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class UsersRoutingModule { -} \ No newline at end of file diff --git a/client/app/src/pages/admin/users/users-tab1/users-tab1.component.html b/client/app/src/pages/admin/users/users-tab1/users-tab1.component.html index d2aa5da783..44fa7dbba9 100644 --- a/client/app/src/pages/admin/users/users-tab1/users-tab1.component.html +++ b/client/app/src/pages/admin/users/users-tab1/users-tab1.component.html @@ -1,62 +1,70 @@
-
- {{ 'Users' | translate }} - -
-
-
-
- - * -
- -
-
-
- - * -
- -
-
-
- - * -
- -
-
-
- - * -
- -
-
- {{ 'Invalid email address' | translate }} -
+
+ {{ 'Users' | translate }} + +
+ @if (showAddUser) { +
+
+
+ + * +
+ +
+
+ @if (new_user.role !== 'receiver' || !nodeResolver.dataModel.simplified_login) { +
+ + * +
+
-
- +
+ } +
+ + * +
+ +
+
+
+ + * +
+ +
+ @if (email?.errors?.['pattern'] && email.invalid && (email.dirty || email.touched)) { +
+ {{ 'Invalid email address' | translate }}
+ } +
+
+
+
+ }
- -
-
- -
+@if (usersData.length > 0) { + @for (user of usersData | orderBy:'name'; track user; let index = $index) { +
+
+ +
- \ No newline at end of file + } +} \ No newline at end of file diff --git a/client/app/src/pages/admin/users/users-tab1/users-tab1.component.ts b/client/app/src/pages/admin/users/users-tab1/users-tab1.component.ts index c56bf4d04c..995d63a097 100644 --- a/client/app/src/pages/admin/users/users-tab1/users-tab1.component.ts +++ b/client/app/src/pages/admin/users/users-tab1/users-tab1.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {NewUser} from "@app/models/admin/new-user"; import {tenantResolverModel} from "@app/models/resolvers/tenant-resolver-model"; import {userResolverModel} from "@app/models/resolvers/user-resolver-model"; @@ -8,12 +8,25 @@ import {TenantsResolver} from "@app/shared/resolvers/tenants.resolver"; import {UsersResolver} from "@app/shared/resolvers/users.resolver"; import {HttpService} from "@app/shared/services/http.service"; import {UtilsService} from "@app/shared/services/utils.service"; +import { NgClass } from "@angular/common"; +import { FormsModule } from "@angular/forms"; +import { UserEditorComponent } from "../user-editor/user-editor.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { OrderByPipe } from "@app/shared/pipes/order-by.pipe"; @Component({ - selector: "src-users-tab1", - templateUrl: "./users-tab1.component.html" + selector: "src-users-tab1", + templateUrl: "./users-tab1.component.html", + standalone: true, + imports: [FormsModule, NgClass, UserEditorComponent, TranslatorPipe, OrderByPipe] }) export class UsersTab1Component implements OnInit { + private httpService = inject(HttpService); + protected nodeResolver = inject(NodeResolver); + private usersResolver = inject(UsersResolver); + private tenantsResolver = inject(TenantsResolver); + private utilsService = inject(UtilsService); + showAddUser = false; tenantData: tenantResolverModel; usersData: userResolverModel[]; @@ -26,9 +39,6 @@ export class UsersTab1Component implements OnInit { editing = false; protected readonly Constants = Constants; - constructor(private httpService: HttpService, protected nodeResolver: NodeResolver, private usersResolver: UsersResolver, private tenantsResolver: TenantsResolver, private utilsService: UtilsService) { - } - ngOnInit(): void { if (this.usersResolver.dataModel) { this.usersData = this.usersResolver.dataModel; diff --git a/client/app/src/pages/admin/users/users-tab2/users-tab2.component.ts b/client/app/src/pages/admin/users/users-tab2/users-tab2.component.ts index 7d06c95510..0700f0f439 100644 --- a/client/app/src/pages/admin/users/users-tab2/users-tab2.component.ts +++ b/client/app/src/pages/admin/users/users-tab2/users-tab2.component.ts @@ -1,17 +1,21 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {nodeResolverModel} from "@app/models/resolvers/node-resolver-model"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {UtilsService} from "@app/shared/services/utils.service"; +import { FormsModule } from "@angular/forms"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-users-tab2", - templateUrl: "./users-tab2.component.html" + selector: "src-users-tab2", + templateUrl: "./users-tab2.component.html", + standalone: true, + imports: [FormsModule, TranslatorPipe] }) export class UsersTab2Component implements OnInit { - nodeData: nodeResolverModel; + private nodeResolver = inject(NodeResolver); + private utilsService = inject(UtilsService); - constructor(private nodeResolver: NodeResolver, private utilsService: UtilsService) { - } + nodeData: nodeResolverModel; ngOnInit(): void { if (this.nodeResolver.dataModel) { diff --git a/client/app/src/pages/admin/users/users.component.html b/client/app/src/pages/admin/users/users.component.html index 1791a7874d..ee4f291e06 100644 --- a/client/app/src/pages/admin/users/users.component.html +++ b/client/app/src/pages/admin/users/users.component.html @@ -1,19 +1,19 @@
- -
- - - - - - + +
+ + + + + +
diff --git a/client/app/src/pages/admin/users/users.component.ts b/client/app/src/pages/admin/users/users.component.ts index a3f816ce87..8a6fa232f6 100644 --- a/client/app/src/pages/admin/users/users.component.ts +++ b/client/app/src/pages/admin/users/users.component.ts @@ -1,23 +1,28 @@ -import {AfterViewInit, Component, TemplateRef, ViewChild, ChangeDetectorRef} from "@angular/core"; +import { AfterViewInit, Component, TemplateRef, ViewChild, ChangeDetectorRef, inject } from "@angular/core"; import {Tab} from "@app/models/component-model/tab"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {UsersTab1Component} from "@app/pages/admin/users/users-tab1/users-tab1.component"; import {UsersTab2Component} from "@app/pages/admin/users/users-tab2/users-tab2.component"; +import { NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgbNavOutlet } from "@ng-bootstrap/ng-bootstrap"; +import { NgTemplateOutlet } from "@angular/common"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-users", - templateUrl: "./users.component.html" + selector: "src-users", + templateUrl: "./users.component.html", + standalone: true, + imports: [NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgTemplateOutlet, NgbNavOutlet, UsersTab1Component, UsersTab2Component, TranslatorPipe] }) export class UsersComponent implements AfterViewInit { + node = inject(NodeResolver); + private cdr = inject(ChangeDetectorRef); + @ViewChild("tab1") tab1!: TemplateRef; @ViewChild("tab2") tab2!: TemplateRef; tabs: Tab[]; nodeData: NodeResolver; active: string; - constructor(public node: NodeResolver, private cdr: ChangeDetectorRef) { - } - ngAfterViewInit(): void { setTimeout(() => { this.active = "Users"; diff --git a/client/app/src/pages/admin/users/users.module.ts b/client/app/src/pages/admin/users/users.module.ts deleted file mode 100644 index 72066f759f..0000000000 --- a/client/app/src/pages/admin/users/users.module.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {UsersRoutingModule} from "@app/pages/admin/users/users-routing.module"; -import {UserEditorComponent} from "@app/pages/admin/users/user-editor/user-editor.component"; -import {FormsModule} from "@angular/forms"; -import {RouterModule} from "@angular/router"; -import {NgbNavModule, NgbModule} from "@ng-bootstrap/ng-bootstrap"; -import {NgSelectModule} from "@ng-select/ng-select"; -import {SharedModule} from "@app/shared.module"; -import {UsersComponent} from "@app/pages/admin/users/users.component"; -import {UsersTab1Component} from "@app/pages/admin/users/users-tab1/users-tab1.component"; -import {UsersTab2Component} from "@app/pages/admin/users/users-tab2/users-tab2.component"; - - -@NgModule({ - declarations: [ - UsersComponent, - UserEditorComponent, - UsersTab1Component, - UsersTab2Component - ], - imports: [ - CommonModule, - UsersRoutingModule, - SharedModule, NgbNavModule, NgbModule, RouterModule, FormsModule, NgSelectModule - ] -}) -export class UsersModule { -} \ No newline at end of file diff --git a/client/app/src/pages/analyst/analyst.module.ts b/client/app/src/pages/analyst/analyst.module.ts deleted file mode 100644 index 6acca00e57..0000000000 --- a/client/app/src/pages/analyst/analyst.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {RouterModule} from "@angular/router"; -import {TranslateModule} from "@ngx-translate/core"; -import {SharedModule} from "@app/shared.module"; -import {FormsModule} from "@angular/forms"; -import {NgbDatepickerModule, NgbDropdownModule, NgbModule, NgbNavModule} from "@ng-bootstrap/ng-bootstrap"; -import {NgMultiSelectDropDownModule} from "ng-multiselect-dropdown"; -import { HomeComponent } from '@app/pages/analyst/home/home.component'; -import { SidebarComponent } from '@app/pages/analyst/sidebar/sidebar.component'; -import { StatisticsComponent } from '@app/pages/analyst/statistics/statistics.component'; -import { BaseChartDirective, provideCharts, withDefaultRegisterables } from 'ng2-charts'; - - -@NgModule({ - declarations:[ - HomeComponent, - SidebarComponent, - StatisticsComponent - ], - imports: [ - CommonModule, RouterModule, TranslateModule, SharedModule, FormsModule, - NgbModule, NgbNavModule,BaseChartDirective, - NgbDatepickerModule, NgbDropdownModule, NgMultiSelectDropDownModule.forRoot() - - ], - providers: [provideCharts(withDefaultRegisterables())], - exports: [SidebarComponent] -}) -export class AnalystModule { } diff --git a/client/app/src/pages/analyst/analyst-routing.module.ts b/client/app/src/pages/analyst/analyst.routes.ts similarity index 54% rename from client/app/src/pages/analyst/analyst-routing.module.ts rename to client/app/src/pages/analyst/analyst.routes.ts index 1161e2ba0b..95dd1cf123 100644 --- a/client/app/src/pages/analyst/analyst-routing.module.ts +++ b/client/app/src/pages/analyst/analyst.routes.ts @@ -1,21 +1,17 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {HomeComponent} from "@app/pages/analyst/home/home.component"; -import {StatisticsComponent} from "@app/pages/analyst/statistics/statistics.component"; -import {PreferencesComponent} from "@app/shared/partials/preferences/preferences.component"; +import {Routes} from "@angular/router"; import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; import {RTipsResolver} from "@app/shared/resolvers/r-tips-resolver.service"; import {StatisticsResolver} from "@app/shared/resolvers/statistics.resolver"; -const routes: Routes = [ +export const analystRoutes: Routes = [ { path: "", - component: HomeComponent, + loadComponent: () => import('@app/pages/analyst/home/home.component').then(m => m.HomeComponent), pathMatch: "full", data: {pageTitle: "Home"}, }, { path: "home", - component: HomeComponent, + loadComponent: () => import('@app/pages/analyst/home/home.component').then(m => m.HomeComponent), pathMatch: "full", resolve: { PreferenceResolver, RTipsResolver @@ -24,7 +20,7 @@ const routes: Routes = [ }, { path: "statistics", - component: StatisticsComponent, + loadComponent: () => import('@app/pages/analyst/statistics/statistics.component').then(m => m.StatisticsComponent), resolve: { StatisticsResolver }, @@ -33,17 +29,11 @@ const routes: Routes = [ }, { path: "preferences", - component: PreferencesComponent, + loadComponent: () => import('@app/shared/partials/preferences/preferences.component').then(m => m.PreferencesComponent), pathMatch: "full", resolve: { PreferenceResolver, RTipsResolver }, data: {pageTitle: "Preferences"}, } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class AnalystRoutingModule { } +]; \ No newline at end of file diff --git a/client/app/src/pages/analyst/home/home.component.ts b/client/app/src/pages/analyst/home/home.component.ts index e727c06851..c756eaaa44 100644 --- a/client/app/src/pages/analyst/home/home.component.ts +++ b/client/app/src/pages/analyst/home/home.component.ts @@ -1,17 +1,22 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {AppDataService} from "@app/app-data.service"; import {preferenceResolverModel} from "@app/models/resolvers/preference-resolver-model"; import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; import {UtilsService} from "@app/shared/services/utils.service"; +import { UserHomeComponent } from "../../../shared/partials/user-home/user-home.component"; @Component({ - selector: "src-analyst-home", - templateUrl: "./home.component.html" + selector: "src-analyst-home", + templateUrl: "./home.component.html", + standalone: true, + imports: [UserHomeComponent] }) export class HomeComponent implements OnInit { + private appDataService = inject(AppDataService); + private utilsService = inject(UtilsService); + private preference = inject(PreferenceResolver); + preferenceData: preferenceResolverModel; - constructor(private appDataService: AppDataService,private utilsService: UtilsService, private preference: PreferenceResolver) { - } ngOnInit(): void { if (this.preference.dataModel) { diff --git a/client/app/src/pages/analyst/sidebar/sidebar.component.ts b/client/app/src/pages/analyst/sidebar/sidebar.component.ts index b244a5eae5..b998f06f03 100644 --- a/client/app/src/pages/analyst/sidebar/sidebar.component.ts +++ b/client/app/src/pages/analyst/sidebar/sidebar.component.ts @@ -1,13 +1,17 @@ -import {Component} from "@angular/core"; -import {Router} from "@angular/router"; +import { Component, inject } from "@angular/core"; +import { Router, RouterLink, RouterLinkActive } from "@angular/router"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-analyst-sidebar", - templateUrl: "./sidebar.component.html" + selector: "src-analyst-sidebar", + templateUrl: "./sidebar.component.html", + standalone: true, + imports: [RouterLink, RouterLinkActive, TranslateModule, TranslatorPipe] }) -export class SidebarComponent { - constructor(private router: Router) { - } +export class AnalystSidebarComponent { + private router = inject(Router); + isActive(route: string): boolean { return this.router.isActive(route, { diff --git a/client/app/src/pages/analyst/statistics/statistics.component.html b/client/app/src/pages/analyst/statistics/statistics.component.html index 8ac200d67a..654eb56f8f 100644 --- a/client/app/src/pages/analyst/statistics/statistics.component.html +++ b/client/app/src/pages/analyst/statistics/statistics.component.html @@ -1,22 +1,24 @@
-
-
-
{{ 'Reports' | translate }}
-
-

{{ charts[0].total }}

-
-
+
+
+
{{ 'Reports' | translate }}
+
+

{{ charts[0].total }}

+
+
-
-
-
{{ chart.title | translate }}
-
- - -
+ @for (chart of charts; track chart; let i = $index) { +
+
+
{{ chart.title | translate }}
+
+ +
+
+ }
diff --git a/client/app/src/pages/analyst/statistics/statistics.component.ts b/client/app/src/pages/analyst/statistics/statistics.component.ts index 5a6b4bff08..909a10a960 100644 --- a/client/app/src/pages/analyst/statistics/statistics.component.ts +++ b/client/app/src/pages/analyst/statistics/statistics.component.ts @@ -1,15 +1,26 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { StatisticsResolver } from '@app/shared/resolvers/statistics.resolver'; -import { TranslateService } from '@ngx-translate/core'; +import { TranslateService, TranslateModule } from '@ngx-translate/core'; + +import { BaseChartDirective, provideCharts, withDefaultRegisterables } from 'ng2-charts'; +import { TranslatorPipe } from '@app/shared/pipes/translate'; @Component({ - selector: 'src-statistics', - templateUrl: './statistics.component.html', + selector: 'src-statistics', + templateUrl: './statistics.component.html', + standalone: true, + imports: [ + BaseChartDirective, + TranslateModule, + TranslatorPipe +], + providers: [provideCharts(withDefaultRegisterables())], }) export class StatisticsComponent implements OnInit { - charts: any[] = []; + private translateService = inject(TranslateService); + private statisticsResolver = inject(StatisticsResolver); - constructor(private translateService: TranslateService, private statisticsResolver: StatisticsResolver) { } + charts: any[] = []; ngOnInit(): void { this.initializeCharts(); diff --git a/client/app/src/pages/app/app.component.html b/client/app/src/pages/app/app.component.html index 3768612249..f0211cb3db 100644 --- a/client/app/src/pages/app/app.component.html +++ b/client/app/src/pages/app/app.component.html @@ -1,67 +1,85 @@ - -
-

Error!



-
You are running an unsupported and potentially vulnerable browser.

In order to use GlobaLeaks you - are strongly advised to download and install the Tor Browser.
The Tor Browser includes various privacy and - security enhancements not present in other browsers.

- [ Download - the Tor Browser ]

This is the list of the other supported browsers:
-
    -
  • Mozilla Firefox >= 38
  • -
  • Google Chrome >= 45
  • -
  • Brave >= 1.20.110
  • -
  • Edge (any)
  • -
  • Internet Explorer 11
  • -
  • Safari: >= 8
  • -
  • iOS >= 9
  • -
  • Android >= 4.4
  • -
-
+@if (!supportedBrowser) { +
+

Error!



+
You are running an unsupported and potentially vulnerable browser.

In order to use GlobaLeaks you + are strongly advised to download and install the Tor Browser.
The Tor Browser includes various privacy and + security enhancements not present in other browsers.

+ [ Download + the Tor Browser ]

This is the list of the other supported browsers:
+
    +
  • Mozilla Firefox >= 38
  • +
  • Google Chrome >= 45
  • +
  • Brave >= 1.20.110
  • +
  • Edge (any)
  • +
  • Internet Explorer 11
  • +
  • Safari: >= 8
  • +
  • iOS >= 9
  • +
  • Android >= 4.4
  • +
- +
+} -
+@if (showLoadingPanel) { +
-
- -
-
- -
-
- -
-
- -
- - -
-
-
- -
- -
+
+} +@if (appDataService.public.node) { +
+ @if (authenticationService.session?.properties?.operator_session) { +
+ +
+ } + @if (!appDataService.public.node.root_tenant && appDataService.public.node.mode === 'demo') { +
+ +
+ } + @if (isWhistleblowerPage() && location.hash!=='#/admin/' && !appDataService.public.node.disable_privacy_badge) { +
+ +
+ } + + +
+
+
+ @if (appConfig.sidebar && showSidebar) { + + } +
+ +
+
- - +
+ +} diff --git a/client/app/src/pages/app/app.component.ts b/client/app/src/pages/app/app.component.ts index 7f9281948a..d1a3a49148 100644 --- a/client/app/src/pages/app/app.component.ts +++ b/client/app/src/pages/app/app.component.ts @@ -1,46 +1,107 @@ -import {AfterViewInit, ChangeDetectorRef, Component, Inject, OnInit, Renderer2} from "@angular/core"; +import { AfterViewInit, ChangeDetectorRef, Component, HostListener, OnDestroy, OnInit, Renderer2, inject } from "@angular/core"; import {AppConfigService} from "@app/services/root/app-config.service"; import {AppDataService} from "@app/app-data.service"; import {UtilsService} from "@app/shared/services/utils.service"; -import {LangChangeEvent, TranslateService} from "@ngx-translate/core"; -import {NavigationEnd, Router} from "@angular/router"; +import { LangChangeEvent, TranslateService, TranslateModule } from "@ngx-translate/core"; +import { NavigationEnd, Router, RouterOutlet } from "@angular/router"; import {BrowserCheckService} from "@app/shared/services/browser-check.service"; import {animate, state, style, transition, trigger} from "@angular/animations"; import {TranslationService} from "@app/services/helper/translation.service"; -import {DOCUMENT} from "@angular/common"; +import { DOCUMENT, NgClass } from "@angular/common"; import {AuthenticationService} from "@app/services/helper/authentication.service"; +import { HeaderComponent } from "../../shared/partials/header/header.component"; +import { NgbCollapse } from "@ng-bootstrap/ng-bootstrap"; +import { TranslatorPipe } from "../../shared/pipes/translate"; +import { FooterComponent } from "@app/shared/partials/footer/footer.component"; +import { PrivacyBadgeComponent } from "@app/shared/partials/privacybadge/privacy-badge.component"; +import { DemoComponent } from "@app/shared/partials/demo/demo.component"; +import { MessageConsoleComponent } from "@app/shared/partials/messageconsole/message-console.component"; +import { OperationComponent } from "@app/shared/partials/operation/operation.component"; +import { AdminSidebarComponent } from "../admin/sidebar/sidebar.component"; +import { AnalystSidebarComponent } from "../analyst/sidebar/sidebar.component"; +import { CustodianSidebarComponent } from "../custodian/sidebar/sidebar.component"; +import { ReceiptSidebarComponent } from "../recipient/sidebar/sidebar.component"; +import { HttpClient } from "@angular/common/http"; +import { registerLocales } from "@app/services/helper/locale-provider"; +import { mockEngine } from "@app/services/helper/mocks"; +import { TranslateHttpLoader } from "@ngx-translate/http-loader"; +import { DEFAULT_INTERRUPTSOURCES, Idle } from "@ng-idle/core"; +import { CryptoService } from "@app/shared/services/crypto.service"; +import { HttpService } from "@app/shared/services/http.service"; +import { Keepalive } from "@ng-idle/keepalive"; +registerLocales(); + +export function createTranslateLoader(http: HttpClient) { + return new TranslateHttpLoader(http, "l10n/", ""); +} + +(window as any).mockEngine = mockEngine; +declare global { + interface Window { + GL: { + language: string; + mockEngine: any; + }; + } +} +window.GL = { + language: 'en', // Assuming a default language + mockEngine: mockEngine +}; @Component({ - selector: "app-root", - templateUrl: "./app.component.html", - animations: [ - trigger('fadeInOut', [ - state('void', style({ - opacity: 0 - })), - transition(':enter, :leave', animate(150)), - ]) - ] + selector: "app-root", + templateUrl: "./app.component.html", + animations: [ + trigger('fadeInOut', [ + state('void', style({ + opacity: 0 + })), + transition(':enter, :leave', animate(150)), + ]) + ], + standalone: true, + imports: [NgClass, HeaderComponent, PrivacyBadgeComponent, AdminSidebarComponent, AnalystSidebarComponent, MessageConsoleComponent, DemoComponent, OperationComponent, CustodianSidebarComponent, ReceiptSidebarComponent, FooterComponent, NgbCollapse, RouterOutlet, TranslateModule, TranslatorPipe] }) -export class AppComponent implements AfterViewInit, OnInit { +export class AppComponent implements AfterViewInit, OnInit, OnDestroy{ + private document = inject(DOCUMENT); + private renderer = inject(Renderer2); + protected browserCheckService = inject(BrowserCheckService); + private changeDetectorRef = inject(ChangeDetectorRef); + private router = inject(Router); + protected translationService = inject(TranslationService); + protected translate = inject(TranslateService); + protected appConfig = inject(AppConfigService); + protected appDataService = inject(AppDataService); + protected utilsService = inject(UtilsService); + protected authenticationService = inject(AuthenticationService); + private cryptoService = inject(CryptoService); + private idle = inject(Idle); + private keepalive = inject(Keepalive); + private httpService = inject(HttpService); + showSidebar: boolean = true; isNavCollapsed: boolean = true; showLoadingPanel = false; supportedBrowser = true; loading = false; - constructor(@Inject(DOCUMENT) private document: Document, private renderer: Renderer2, protected browserCheckService: BrowserCheckService, private changeDetectorRef: ChangeDetectorRef, private router: Router, protected translationService: TranslationService, protected translate: TranslateService, protected appConfig: AppConfigService, protected appDataService: AppDataService, protected utilsService: UtilsService, protected authenticationService: AuthenticationService) { + + constructor() { + this.initIdleState(); this.watchLanguage(); + (window as any).scope = this.appDataService; } watchLanguage() { this.translate.onLangChange.subscribe((event: LangChangeEvent) => { document.getElementsByTagName("html")[0].setAttribute("lang", this.translate.currentLang); + this.translationService.handleLTRandRTLStyling(event, this.renderer); }); } checkToShowSidebar() { - this.router.events.subscribe(event => { + this.router.events.subscribe((event:any) => { if (event instanceof NavigationEnd) { const excludedUrls = [ "/recipient/reports" @@ -56,23 +117,61 @@ export class AppComponent implements AfterViewInit, OnInit { this.checkToShowSidebar(); } - isWhistleblowerPage() { - const temp = this.utilsService.isWhistleblowerPage(this.authenticationService, this.appDataService) - if ((this.router.url === "/" || this.router.url === "/submission") && this.loading) { - return true; - } else { - this.loading = temp; - return this.loading; - } + isWhistleblowerPage(): boolean { + const currentHash = location.hash; + return currentHash === "#/" || currentHash === "#/submission"; } + public ngAfterViewInit(): void { - this.appDataService.showLoadingPanel$.subscribe((value) => { + this.appDataService.showLoadingPanel$.subscribe((value:any) => { this.showLoadingPanel = value; this.supportedBrowser = this.browserCheckService.checkBrowserSupport(); this.changeDetectorRef.detectChanges(); }); } + @HostListener("window:beforeunload") + async ngOnDestroy() { + this.reset(); + } + + initIdleState() { + this.idle.setIdle(1500); + this.idle.setTimeout(300); + this.keepalive.interval(30); + this.idle.setInterrupts(DEFAULT_INTERRUPTSOURCES); + + this.keepalive.onPing.subscribe(() => { + if (this.authenticationService && this.authenticationService.session) { + const token = this.authenticationService.session.token; + this.cryptoService.proofOfWork(token.id).subscribe((result:any) => { + const param = {'token': token.id + ":" + result}; + this.httpService.requestRefreshUserSession(param).subscribe(((result:any) => { + this.authenticationService.session.token = result.token; + })); + }); + } + }); + + this.idle.onTimeout.subscribe(() => { + if (this.authenticationService && this.authenticationService.session) { + if (this.authenticationService.session.role === "whistleblower") { + window.location.replace("about:blank"); + } else { + this.authenticationService.deleteSession(); + this.authenticationService.loginRedirect(); + } + } + }); + + this.reset(); + } + + reset() { + this.idle.watch(); + this.authenticationService.reset(); + } + protected readonly location = location; } diff --git a/client/app/src/pages/auth/auth-routing.module.ts b/client/app/src/pages/auth/auth-routing.module.ts deleted file mode 100644 index 6ba8a9601a..0000000000 --- a/client/app/src/pages/auth/auth-routing.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {LoginComponent} from "@app/pages/auth/login/login.component"; -import {PasswordResetComponent} from "@app/pages/auth/password-reset/password-reset.component"; -import {PasswordRequestedComponent} from "@app/pages/auth/passwordreqested/password-requested.component"; - -const routes: Routes = [ - { - path: "", - component: LoginComponent, - pathMatch: "full", - data: {pageTitle: "Log in"}, - }, - { - path: "passwordreset", - component: PasswordResetComponent, - pathMatch: "full", - data: {pageTitle: "Password reset"}, - }, - { - path: "passwordreset/requested", - component: PasswordRequestedComponent, - pathMatch: "full", - data: {pageTitle: "Password reset"}, - } - -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], -}) -export class AuthRoutingModule { -} diff --git a/client/app/src/pages/auth/auth.module.ts b/client/app/src/pages/auth/auth.module.ts deleted file mode 100644 index cf308ace71..0000000000 --- a/client/app/src/pages/auth/auth.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {LoginComponent} from "@app/pages/auth/login/login.component"; -import {SimpleLoginComponent} from "@app/pages/auth/login/templates/simple-login/simple-login.component"; -import {DefaultLoginComponent} from "@app/pages/auth/login/templates/default-login/default-login.component"; -import {FormsModule, ReactiveFormsModule} from "@angular/forms"; -import {NgSelectModule} from "@ng-select/ng-select"; -import {PasswordResetComponent} from "@app/pages/auth/password-reset/password-reset.component"; -import {PasswordRequestedComponent} from "@app/pages/auth/passwordreqested/password-requested.component"; -import { - PasswordResetResponseComponent -} from "@app/pages/auth/password-reset-response/password-reset-response.component"; -import {TranslateModule} from "@ngx-translate/core"; -import {SharedModule} from "@app/shared.module"; - -@NgModule({ - declarations: [ - LoginComponent, - SimpleLoginComponent, - DefaultLoginComponent, - PasswordResetComponent, - PasswordRequestedComponent, - PasswordResetResponseComponent, - ], - imports: [ - CommonModule, - TranslateModule, - FormsModule, - ReactiveFormsModule, - NgSelectModule, - SharedModule - ] -}) -export class AuthModule { -} diff --git a/client/app/src/pages/auth/auth.routes.ts b/client/app/src/pages/auth/auth.routes.ts new file mode 100644 index 0000000000..8d5e6343c2 --- /dev/null +++ b/client/app/src/pages/auth/auth.routes.ts @@ -0,0 +1,22 @@ +import {Routes} from "@angular/router"; + +export const authRoutes: Routes = [ + { + path: "", + loadComponent: () => import('@app/pages/auth/login/login.component').then(m => m.LoginComponent), + pathMatch: "full", + data: {pageTitle: "Log in"}, + }, + { + path: "passwordreset", + loadComponent: () => import('@app/pages/auth/password-reset/password-reset.component').then(m => m.PasswordResetComponent), + pathMatch: "full", + data: {pageTitle: "Password reset"}, + }, + { + path: "passwordreset/requested", + loadComponent: () => import('@app/pages/auth/passwordreqested/password-requested.component').then(m => m.PasswordRequestedComponent), + pathMatch: "full", + data: {pageTitle: "Password reset"}, + } +]; \ No newline at end of file diff --git a/client/app/src/pages/auth/login/login.component.html b/client/app/src/pages/auth/login/login.component.html index 4fa7795cc2..a335686bfe 100644 --- a/client/app/src/pages/auth/login/login.component.html +++ b/client/app/src/pages/auth/login/login.component.html @@ -1,16 +1,24 @@
- +
diff --git a/client/app/src/pages/auth/login/login.component.ts b/client/app/src/pages/auth/login/login.component.ts index 94dff88e01..5ca805a4c6 100644 --- a/client/app/src/pages/auth/login/login.component.ts +++ b/client/app/src/pages/auth/login/login.component.ts @@ -1,22 +1,32 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {LoginDataRef} from "@app/pages/auth/login/model/login-model"; import {ActivatedRoute, Router} from "@angular/router"; import {AppDataService} from "@app/app-data.service"; +import { FormsModule } from "@angular/forms"; + +import { SimpleLoginComponent } from "./templates/simple-login/simple-login.component"; +import { DefaultLoginComponent } from "./templates/default-login/default-login.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "app-login", - templateUrl: "./login.component.html" + selector: "app-login", + templateUrl: "./login.component.html", + standalone: true, + imports: [FormsModule, SimpleLoginComponent, DefaultLoginComponent, TranslateModule, TranslatorPipe] }) export class LoginComponent implements OnInit { + private authentication = inject(AuthenticationService); + router = inject(Router); + private route = inject(ActivatedRoute); + protected appDataService = inject(AppDataService); + protected readonly location = location; loginData = new LoginDataRef(); - constructor(private authentication: AuthenticationService, public router: Router, private route: ActivatedRoute, protected appDataService: AppDataService) { - } - ngOnInit() { this.route.queryParams.subscribe(params => { if ("token" in params) { diff --git a/client/app/src/pages/auth/login/templates/default-login/default-login.component.html b/client/app/src/pages/auth/login/templates/default-login/default-login.component.html index b1ffcc1ef1..1d0d84298f 100644 --- a/client/app/src/pages/auth/login/templates/default-login/default-login.component.html +++ b/client/app/src/pages/auth/login/templates/default-login/default-login.component.html @@ -1,42 +1,46 @@ -
+@if (!authentication.requireAuthCode) { +
-
- - - - -
+
+ + + + +
-
- - - - -
+
+ + + + +
- {{'Forgot password?'|translate}} + {{'Forgot password?'|translate}} -
-
+
+} +@if (authentication.requireAuthCode) { +
- +
- -
- - - - -
+ +
+ + + + +
-
+
+} \ No newline at end of file diff --git a/client/app/src/pages/auth/login/templates/default-login/default-login.component.ts b/client/app/src/pages/auth/login/templates/default-login/default-login.component.ts index 61c1ab0e73..6b8526e4a9 100644 --- a/client/app/src/pages/auth/login/templates/default-login/default-login.component.ts +++ b/client/app/src/pages/auth/login/templates/default-login/default-login.component.ts @@ -1,20 +1,30 @@ -import {Component, Input} from "@angular/core"; +import { Component, Input, inject } from "@angular/core"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {LoginDataRef} from "@app/pages/auth/login/model/login-model"; import {UtilsService} from "@app/shared/services/utils.service"; -import {ControlContainer, NgForm} from "@angular/forms"; +import { ControlContainer, NgForm, FormsModule } from "@angular/forms"; import {AppDataService} from "@app/app-data.service"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; + @Component({ - selector: "app-default-login", - templateUrl: "./default-login.component.html", - viewProviders: [{provide: ControlContainer, useExisting: NgForm}], + selector: "app-default-login", + templateUrl: "./default-login.component.html", + viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], + standalone: true, + imports: [ + FormsModule, + TranslateModule, + TranslatorPipe +], }) export class DefaultLoginComponent { + protected utils = inject(UtilsService); + protected authentication = inject(AuthenticationService); + protected appDataService = inject(AppDataService); + @Input() loginData: LoginDataRef; @Input() loginValidator: NgForm; - - constructor(protected utils: UtilsService, protected authentication: AuthenticationService, protected appDataService: AppDataService) { - } } diff --git a/client/app/src/pages/auth/login/templates/simple-login/simple-login.component.html b/client/app/src/pages/auth/login/templates/simple-login/simple-login.component.html index a770b780f7..d5aa56c985 100644 --- a/client/app/src/pages/auth/login/templates/simple-login/simple-login.component.html +++ b/client/app/src/pages/auth/login/templates/simple-login/simple-login.component.html @@ -1,49 +1,61 @@ -
-
+@if (!authentication.requireAuthCode) { +
+ @if (appDataService.public.receivers.length === 1) { +
- +
- - - {{'Forgot password?'|translate}} - + + + {{'Forgot password?'|translate}} +
-
-
+
+ } + @if (appDataService.public.receivers.length !== 1) { +
- - - {{item.name}} - + + + @for (item of appDataService.public.receivers; track item) { + {{item.name}} + } +
-
+ @if (authentication.loginData.loginUsername) { +
- - + +
- - - {{'Forgot password?'|translate}} - + + + {{'Forgot password?'|translate}} +
-
-
-
-
+
+ } +
+ } +
+} +@if (authentication.requireAuthCode) { +
- - + +
-
+
+} diff --git a/client/app/src/pages/auth/login/templates/simple-login/simple-login.component.ts b/client/app/src/pages/auth/login/templates/simple-login/simple-login.component.ts index 5f471b023d..f5ccf47f26 100644 --- a/client/app/src/pages/auth/login/templates/simple-login/simple-login.component.ts +++ b/client/app/src/pages/auth/login/templates/simple-login/simple-login.component.ts @@ -1,21 +1,33 @@ -import {Component, Input, OnInit} from "@angular/core"; +import { Component, Input, OnInit, inject } from "@angular/core"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {LoginDataRef} from "@app/pages/auth/login/model/login-model"; -import {NgForm} from "@angular/forms"; +import { NgForm, FormsModule } from "@angular/forms"; import {AppDataService} from "@app/app-data.service"; +import { NgSelectComponent, NgOptionComponent } from "@ng-select/ng-select"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; + @Component({ - selector: "app-simple-login", - templateUrl: "./simple-login.component.html", + selector: "app-simple-login", + templateUrl: "./simple-login.component.html", + standalone: true, + imports: [ + FormsModule, + NgSelectComponent, + NgOptionComponent, + TranslateModule, + TranslatorPipe +], }) export class SimpleLoginComponent implements OnInit { + protected authentication = inject(AuthenticationService); + protected appDataService = inject(AppDataService); + @Input() loginData: LoginDataRef; @Input() loginValidator: NgForm; - constructor(protected authentication: AuthenticationService, protected appDataService: AppDataService) { - } - public ngOnInit(): void { } diff --git a/client/app/src/pages/auth/password-reset-response/password-reset-response.component.html b/client/app/src/pages/auth/password-reset-response/password-reset-response.component.html index 547931fc95..75f91ddc3d 100644 --- a/client/app/src/pages/auth/password-reset-response/password-reset-response.component.html +++ b/client/app/src/pages/auth/password-reset-response/password-reset-response.component.html @@ -1,40 +1,44 @@ -
+@if (state === 'require_recovery_key') { +
-
- -
+
+ +
-
-
-
- -
- - - - -
-
-
-
+
+
+
+ +
+ + + + +
+
+
+
-
-
+
+} +@if (state === 'require_two_factor_authentication') { +
-
-
- - -
-
- - - - -
+
+
+ + +
+
+ + + +
+
-
\ No newline at end of file +
+} \ No newline at end of file diff --git a/client/app/src/pages/auth/password-reset-response/password-reset-response.component.ts b/client/app/src/pages/auth/password-reset-response/password-reset-response.component.ts index 9b793163fd..b66b8860df 100644 --- a/client/app/src/pages/auth/password-reset-response/password-reset-response.component.ts +++ b/client/app/src/pages/auth/password-reset-response/password-reset-response.component.ts @@ -1,21 +1,30 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {PasswordRecoveryModel} from "@app/models/authentication/password-recovery-model"; import {ActivatedRoute, Router} from "@angular/router"; import {HttpService} from "@app/shared/services/http.service"; import {UtilsService} from "@app/shared/services/utils.service"; import {AppDataService} from "@app/app-data.service"; +import { FormsModule } from "@angular/forms"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; + @Component({ - selector: "src-password-reset-response", - templateUrl: "./password-reset-response.component.html" + selector: "src-password-reset-response", + templateUrl: "./password-reset-response.component.html", + standalone: true, + imports: [FormsModule, TranslateModule, TranslatorPipe] }) export class PasswordResetResponseComponent implements OnInit { + protected appDataService = inject(AppDataService); + private route = inject(ActivatedRoute); + private httpService = inject(HttpService); + private router = inject(Router); + protected utilsService = inject(UtilsService); + state = "start"; request = new PasswordRecoveryModel(); - constructor(protected appDataService: AppDataService, private route: ActivatedRoute, private httpService: HttpService, private router: Router, protected utilsService: UtilsService) { - } - submit() { const requestObservable = this.httpService.requestChangePassword(JSON.stringify({ "reset_token": this.request.reset_token, diff --git a/client/app/src/pages/auth/password-reset/password-reset.component.html b/client/app/src/pages/auth/password-reset/password-reset.component.html index d977c7c239..19b85aa197 100644 --- a/client/app/src/pages/auth/password-reset/password-reset.component.html +++ b/client/app/src/pages/auth/password-reset/password-reset.component.html @@ -1,22 +1,26 @@
-
- - -
+
+ @if (!appDataService.public.node.simplified_login) { + + } + @if (appDataService.public.node.simplified_login) { + + } +

-
+
-
-
- - - - - - - -
-
+
+
+ + + + + + + +
+
-
\ No newline at end of file +
\ No newline at end of file diff --git a/client/app/src/pages/auth/password-reset/password-reset.component.ts b/client/app/src/pages/auth/password-reset/password-reset.component.ts index 8ea5933b68..076c485c6a 100644 --- a/client/app/src/pages/auth/password-reset/password-reset.component.ts +++ b/client/app/src/pages/auth/password-reset/password-reset.component.ts @@ -1,17 +1,24 @@ -import {Component} from "@angular/core"; +import { Component, inject } from "@angular/core"; import {AppDataService} from "@app/app-data.service"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {UtilsService} from "@app/shared/services/utils.service"; +import { FormsModule } from "@angular/forms"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; + @Component({ - selector: "src-password-reset", - templateUrl: "./password-reset.component.html" + selector: "src-password-reset", + templateUrl: "./password-reset.component.html", + standalone: true, + imports: [FormsModule, TranslateModule, TranslatorPipe] }) export class PasswordResetComponent { - username: string | undefined = undefined; + private authenticationService = inject(AuthenticationService); + protected utilsService = inject(UtilsService); + protected appDataService = inject(AppDataService); - constructor(private authenticationService: AuthenticationService, protected utilsService: UtilsService, protected appDataService: AppDataService) { - } + username: string | undefined = undefined; submitRequest() { if (this.username !== undefined) { diff --git a/client/app/src/pages/auth/passwordreqested/password-requested.component.ts b/client/app/src/pages/auth/passwordreqested/password-requested.component.ts index 96de1275b8..34c971d7c8 100644 --- a/client/app/src/pages/auth/passwordreqested/password-requested.component.ts +++ b/client/app/src/pages/auth/passwordreqested/password-requested.component.ts @@ -1,8 +1,12 @@ import {Component} from "@angular/core"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-passwordreqested", - templateUrl: "./password-requested.component.html" + selector: "src-passwordreqested", + templateUrl: "./password-requested.component.html", + standalone: true, + imports: [TranslateModule, TranslatorPipe] }) export class PasswordRequestedComponent { diff --git a/client/app/src/pages/custodian/custodian-routing.module.ts b/client/app/src/pages/custodian/custodian-routing.module.ts deleted file mode 100644 index 15aebefc98..0000000000 --- a/client/app/src/pages/custodian/custodian-routing.module.ts +++ /dev/null @@ -1,55 +0,0 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {PreferencesComponent} from "@app/shared/partials/preferences/preferences.component"; -import {HomeComponent} from "@app/pages/custodian/home/home.component"; -import {SettingsComponent} from "@app/pages/custodian/settings/settings.component"; -import { - IdentityAccessRequestsComponent -} from "@app/pages/custodian/identity-access-requests/identity-access-requests.component"; -import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; -import {NodeResolver} from "@app/shared/resolvers/node.resolver"; -import {RTipsResolver} from "@app/shared/resolvers/r-tips-resolver.service"; -import {IarResolver} from "@app/shared/resolvers/iar-resolver.service"; - -const routes: Routes = [ - { - path: "preferences", - component: PreferencesComponent, - pathMatch: "full", - data: {pageTitle: "Preferences"}, - }, - { - path: "home", - component: HomeComponent, - pathMatch: "full", - data: {pageTitle: "Home"}, - }, - { - path: "", - component: HomeComponent, - pathMatch: "full", - data: {pageTitle: "Home"}, - }, - { - path: "settings", - component: SettingsComponent, - pathMatch: "full", - data: {pageTitle: "Sites"}, - }, - { - path: "requests", - component: IdentityAccessRequestsComponent, - pathMatch: "full", - data: {pageTitle: "Requests"}, - resolve: { - PreferenceResolver, NodeResolver, RtipsResolver: RTipsResolver, IarsResolver: IarResolver - }, - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], -}) -export class CustodianRoutingModule { -} diff --git a/client/app/src/pages/custodian/custodian.module.ts b/client/app/src/pages/custodian/custodian.module.ts deleted file mode 100644 index 52d8770784..0000000000 --- a/client/app/src/pages/custodian/custodian.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {RouterModule} from "@angular/router"; -import {TranslateModule} from "@ngx-translate/core"; -import {SharedModule} from "@app/shared.module"; -import {FormsModule} from "@angular/forms"; -import {NgbDatepickerModule, NgbDropdownModule, NgbModule, NgbNavModule} from "@ng-bootstrap/ng-bootstrap"; -import {NgMultiSelectDropDownModule} from "ng-multiselect-dropdown"; -import {HomeComponent} from "@app/pages/custodian/home/home.component"; -import {SettingsComponent} from "@app/pages/custodian/settings/settings.component"; -import {SidebarComponent} from "@app/pages/custodian/sidebar/sidebar.component"; -import { - IdentityAccessRequestsComponent -} from "@app/pages/custodian/identity-access-requests/identity-access-requests.component"; -import {SettingsModule} from "@app/pages/admin/settings/settings.module"; - - -@NgModule({ - declarations: [ - HomeComponent, - SettingsComponent, - SidebarComponent, - IdentityAccessRequestsComponent - ], - imports: [ - CommonModule, RouterModule, TranslateModule, SharedModule, FormsModule, - NgbModule, NgbNavModule, - NgbDatepickerModule, NgbDropdownModule, NgMultiSelectDropDownModule.forRoot(), SettingsModule - ], - exports: [SidebarComponent] -}) -export class CustodianModule { -} diff --git a/client/app/src/pages/custodian/custodian.routes.ts b/client/app/src/pages/custodian/custodian.routes.ts new file mode 100644 index 0000000000..ae1d491fce --- /dev/null +++ b/client/app/src/pages/custodian/custodian.routes.ts @@ -0,0 +1,41 @@ +import {Routes} from "@angular/router"; +import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; +import {NodeResolver} from "@app/shared/resolvers/node.resolver"; +import {RTipsResolver} from "@app/shared/resolvers/r-tips-resolver.service"; +import {IarResolver} from "@app/shared/resolvers/iar-resolver.service"; + +export const custodianRoutes: Routes = [ + { + path: "preferences", + loadComponent: () => import('@app/shared/partials/preferences/preferences.component').then(m => m.PreferencesComponent), + pathMatch: "full", + data: {pageTitle: "Preferences"}, + }, + { + path: "home", + loadComponent: () => import('@app/pages/custodian/home/home.component').then(m => m.HomeComponent), + pathMatch: "full", + data: {pageTitle: "Home"}, + }, + { + path: "", + loadComponent: () => import('@app/pages/custodian/home/home.component').then(m => m.HomeComponent), + pathMatch: "full", + data: {pageTitle: "Home"}, + }, + { + path: "settings", + loadComponent: () => import('@app/pages/custodian/settings/settings.component').then(m => m.CustodianSettingsComponent), + pathMatch: "full", + data: {pageTitle: "Settings"}, + }, + { + path: "requests", + loadComponent: () => import('@app/pages/custodian/identity-access-requests/identity-access-requests.component').then(m => m.IdentityAccessRequestsComponent), + pathMatch: "full", + data: {pageTitle: "Requests"}, + resolve: { + PreferenceResolver, NodeResolver, RtipsResolver: RTipsResolver, IarsResolver: IarResolver + }, + } +]; \ No newline at end of file diff --git a/client/app/src/pages/custodian/home/home.component.ts b/client/app/src/pages/custodian/home/home.component.ts index cf02e8a2d7..058d9da1c4 100644 --- a/client/app/src/pages/custodian/home/home.component.ts +++ b/client/app/src/pages/custodian/home/home.component.ts @@ -1,17 +1,22 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {AppDataService} from "@app/app-data.service"; import {preferenceResolverModel} from "@app/models/resolvers/preference-resolver-model"; import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; import {UtilsService} from "@app/shared/services/utils.service"; +import { UserHomeComponent } from "../../../shared/partials/user-home/user-home.component"; @Component({ - selector: "src-custodian-home", - templateUrl: "./home.component.html" + selector: "src-custodian-home", + templateUrl: "./home.component.html", + standalone: true, + imports: [UserHomeComponent] }) export class HomeComponent implements OnInit { + private appDataService = inject(AppDataService); + private utilsService = inject(UtilsService); + private preference = inject(PreferenceResolver); + preferenceData: preferenceResolverModel; - constructor(private appDataService: AppDataService,private utilsService: UtilsService, private preference: PreferenceResolver) { - } ngOnInit(): void { if (this.preference.dataModel) { diff --git a/client/app/src/pages/custodian/identity-access-requests/identity-access-requests.component.html b/client/app/src/pages/custodian/identity-access-requests/identity-access-requests.component.html index 5d668799eb..ee33cc8ed7 100644 --- a/client/app/src/pages/custodian/identity-access-requests/identity-access-requests.component.html +++ b/client/app/src/pages/custodian/identity-access-requests/identity-access-requests.component.html @@ -1,59 +1,63 @@
-
-
- - - - - - - +
+
+ + + + + + -
+
-
-
- - - - - - - - - - - - - - - - - - - - - - - -
{{'Request date'|translate}}{{'ID'|translate}}{{'Report date'|translate}}{{'User'|translate}}{{'Request motivation'|translate}}{{'Reply motivation'|translate}}{{'Authorization'|translate}}
{{iar.request_date | date:'dd-MM-yyyy HH:mm'}}{{iar.submission_progressive}}{{iar.submission_date | date:'dd-MM-yyyy HH:mm'}}{{iar.request_user_name}}{{iar.request_motivation}}{{iar.reply_motivation}} -
- - {{ 'Authorized' | translate }} - - - {{ 'Denied' | translate }} - - - - - -
-
-
+
+
+
+ + + + + + + + + + + + + + @for (iar of iarResolver.dataModel; track iar; let index = $index) { + + + + + + + + + + } + +
{{'Request date'|translate}}{{'ID'|translate}}{{'Report date'|translate}}{{'User'|translate}}{{'Request motivation'|translate}}{{'Reply motivation'|translate}}{{'Authorization'|translate}}
{{iar.request_date | date:'dd-MM-yyyy HH:mm'}}{{iar.submission_progressive}}{{iar.submission_date | date:'dd-MM-yyyy HH:mm'}}{{iar.request_user_name}}{{iar.request_motivation}}{{iar.reply_motivation}} +
+ @switch (iar.reply) { + @case ('authorized') { + {{ 'Authorized' | translate }} + } + @case ('denied') { + {{ 'Denied' | translate }} + } + @case ('pending') { + + + } + } +
+
+
\ No newline at end of file diff --git a/client/app/src/pages/custodian/identity-access-requests/identity-access-requests.component.ts b/client/app/src/pages/custodian/identity-access-requests/identity-access-requests.component.ts index 159093853c..870a52d16a 100644 --- a/client/app/src/pages/custodian/identity-access-requests/identity-access-requests.component.ts +++ b/client/app/src/pages/custodian/identity-access-requests/identity-access-requests.component.ts @@ -1,4 +1,4 @@ -import {Component} from "@angular/core"; +import { Component, inject } from "@angular/core"; import {IarResolver} from "@app/shared/resolvers/iar-resolver.service"; import {UtilsService} from "@app/shared/services/utils.service"; import {HttpService} from "@app/shared/services/http.service"; @@ -6,15 +6,22 @@ import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; import { TipOperationFileIdentityAccessReplyComponent } from "@app/shared/modals/tip-operation-file-identity-access-reply/tip-operation-file-identity-access-reply.component"; +import { DatePipe } from "@angular/common"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-identity-access-requests", - templateUrl: "./identity-access-requests.component.html" + selector: "src-identity-access-requests", + templateUrl: "./identity-access-requests.component.html", + standalone: true, + imports: [DatePipe, TranslateModule, TranslatorPipe] }) export class IdentityAccessRequestsComponent { + private modalService = inject(NgbModal); + private httpService = inject(HttpService); + protected iarResolver = inject(IarResolver); + protected utilsService = inject(UtilsService); - constructor(private modalService: NgbModal, private httpService: HttpService, protected iarResolver: IarResolver, protected utilsService: UtilsService) { - } authorizeIdentityAccessRequest(iar_id: string) { this.httpService.authorizeIdentity("api/custodian/iars/" + iar_id, { diff --git a/client/app/src/pages/custodian/settings/settings.component.html b/client/app/src/pages/custodian/settings/settings.component.html index b327d1dc18..c4cb25d47e 100644 --- a/client/app/src/pages/custodian/settings/settings.component.html +++ b/client/app/src/pages/custodian/settings/settings.component.html @@ -1,16 +1,16 @@
- -
- - - + +
+ + +
\ No newline at end of file diff --git a/client/app/src/pages/custodian/settings/settings.component.ts b/client/app/src/pages/custodian/settings/settings.component.ts index 383495c218..43f4d16559 100644 --- a/client/app/src/pages/custodian/settings/settings.component.ts +++ b/client/app/src/pages/custodian/settings/settings.component.ts @@ -1,12 +1,19 @@ import {AfterViewInit, Component, TemplateRef, ViewChild} from "@angular/core"; import {Tab} from "@app/models/component-model/tab"; import {Tab1Component} from "@app/pages/admin/settings/tab1/tab1.component"; +import { FormsModule } from "@angular/forms"; +import { NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgbNavOutlet } from "@ng-bootstrap/ng-bootstrap"; +import { NgTemplateOutlet } from "@angular/common"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-custodian-settings", - templateUrl: "./settings.component.html" + selector: "src-custodian-settings", + templateUrl: "./settings.component.html", + standalone: true, + imports: [FormsModule, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgTemplateOutlet, NgbNavOutlet, Tab1Component, TranslateModule, TranslatorPipe] }) -export class SettingsComponent implements AfterViewInit { +export class CustodianSettingsComponent implements AfterViewInit { @ViewChild("tab1") tab1!: TemplateRef; tabs: Tab[]; active: string; diff --git a/client/app/src/pages/custodian/sidebar/sidebar.component.ts b/client/app/src/pages/custodian/sidebar/sidebar.component.ts index 5466115cb2..8d16cce39d 100644 --- a/client/app/src/pages/custodian/sidebar/sidebar.component.ts +++ b/client/app/src/pages/custodian/sidebar/sidebar.component.ts @@ -1,13 +1,16 @@ -import {Component} from "@angular/core"; -import {Router} from "@angular/router"; +import { Component, inject } from "@angular/core"; +import { Router, RouterLink, RouterLinkActive } from "@angular/router"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-custodian-sidebar", - templateUrl: "./sidebar.component.html" + selector: "src-custodian-sidebar", + templateUrl: "./sidebar.component.html", + standalone: true, + imports: [RouterLink, RouterLinkActive, TranslateModule, TranslatorPipe] }) -export class SidebarComponent { - constructor(private router: Router) { - } +export class CustodianSidebarComponent { + private router = inject(Router); isActive(route: string): boolean { return this.router.isActive(route, { diff --git a/client/app/src/pages/dashboard/home/home.component.html b/client/app/src/pages/dashboard/home/home.component.html index cf7b374af1..3b74696abc 100644 --- a/client/app/src/pages/dashboard/home/home.component.html +++ b/client/app/src/pages/dashboard/home/home.component.html @@ -1,16 +1,27 @@ - -
+ +@switch (appDataService.page) { + @case ('blank') { +
-
- + } + @case ('tippage') { +
+
-
- + } + @case ('submissionpage') { +
+
-
- + } + @case ('receiptpage') { +
+
-
- + } + @default { +
+
- \ No newline at end of file + } +} diff --git a/client/app/src/pages/dashboard/home/home.component.ts b/client/app/src/pages/dashboard/home/home.component.ts index 1262cef261..5b10f93c4c 100644 --- a/client/app/src/pages/dashboard/home/home.component.ts +++ b/client/app/src/pages/dashboard/home/home.component.ts @@ -1,11 +1,17 @@ -import {Component} from "@angular/core"; +import { Component, inject } from "@angular/core"; import {AppDataService} from "@app/app-data.service"; +import { TippageComponent } from "../../whistleblower/tippage/tippage.component"; +import { SubmissionComponent } from "../../whistleblower/submission/submission.component"; +import { ReceiptComponent } from "../../whistleblower/receipt/receipt.component"; +import { HomepageComponent } from "../../whistleblower/homepage/homepage.component"; + @Component({ - selector: "src-dashboard-home", - templateUrl: "./home.component.html" + selector: "src-dashboard-home", + templateUrl: "./home.component.html", + standalone: true, + imports: [TippageComponent, SubmissionComponent, ReceiptComponent, HomepageComponent] }) export class HomeComponent { - constructor(protected appDataService: AppDataService) { - } + protected appDataService = inject(AppDataService); } diff --git a/client/app/src/pages/recipient/home/home.component.ts b/client/app/src/pages/recipient/home/home.component.ts index eff1d58d05..7badeacf5f 100644 --- a/client/app/src/pages/recipient/home/home.component.ts +++ b/client/app/src/pages/recipient/home/home.component.ts @@ -1,17 +1,22 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {AppDataService} from "@app/app-data.service"; import {preferenceResolverModel} from "@app/models/resolvers/preference-resolver-model"; import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; import { UtilsService } from "@app/shared/services/utils.service"; +import { UserHomeComponent } from "../../../shared/partials/user-home/user-home.component"; @Component({ - selector: "src-recipient-home", - templateUrl: "./home.component.html" + selector: "src-recipient-home", + templateUrl: "./home.component.html", + standalone: true, + imports: [UserHomeComponent] }) export class HomeComponent implements OnInit { + private appDataService = inject(AppDataService); + private utilsService = inject(UtilsService); + private preference = inject(PreferenceResolver); + preferenceData: preferenceResolverModel; - constructor(private appDataService: AppDataService,private utilsService: UtilsService, private preference: PreferenceResolver) { - } ngOnInit(): void { if (this.preference.dataModel) { diff --git a/client/app/src/pages/recipient/recipient.module.ts b/client/app/src/pages/recipient/recipient.module.ts deleted file mode 100644 index 318eebe678..0000000000 --- a/client/app/src/pages/recipient/recipient.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {NgModule} from "@angular/core"; - -import {CommonModule} from "@angular/common"; -import {HomeComponent} from "@app/pages/recipient/home/home.component"; -import {SidebarComponent} from "@app/pages/recipient/sidebar/sidebar.component"; -import {RouterModule} from "@angular/router"; -import {TranslateModule} from "@ngx-translate/core"; -import {SharedModule} from "@app/shared.module"; -import {TipsComponent} from "@app/pages/recipient/tips/tips.component"; -import {TipComponent} from "@app/pages/recipient/tip/tip.component"; -import {SettingsComponent} from "@app/pages/recipient/settings/settings.component"; -import {FormsModule} from "@angular/forms"; -import {NgbDatepickerModule, NgbDropdownModule, NgbModule, NgbNavModule} from "@ng-bootstrap/ng-bootstrap"; -import { - WhistleBlowerIdentityReceiverComponent -} from "@app/pages/recipient/whistleblower-identity-reciever/whistle-blower-identity-receiver.component"; -import {NgMultiSelectDropDownModule} from "ng-multiselect-dropdown"; - -@NgModule({ - declarations: [ - HomeComponent, - SidebarComponent, - TipsComponent, - TipComponent, - SettingsComponent, - WhistleBlowerIdentityReceiverComponent, - ], - imports: [ - CommonModule, RouterModule, TranslateModule, SharedModule, FormsModule, - NgbModule, NgbNavModule, - NgbDatepickerModule, NgbDropdownModule, NgMultiSelectDropDownModule.forRoot() - - ], - exports: [SidebarComponent] -}) -export class RecipientModule { -} diff --git a/client/app/src/pages/recipient/recipient-routing.module.ts b/client/app/src/pages/recipient/recipient.routes.ts similarity index 56% rename from client/app/src/pages/recipient/recipient-routing.module.ts rename to client/app/src/pages/recipient/recipient.routes.ts index 768ccd7e0f..e055926a05 100644 --- a/client/app/src/pages/recipient/recipient-routing.module.ts +++ b/client/app/src/pages/recipient/recipient.routes.ts @@ -1,17 +1,12 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {HomeComponent} from "@app/pages/recipient/home/home.component"; -import {TipsComponent} from "@app/pages/recipient/tips/tips.component"; -import {SettingsComponent} from "@app/pages/recipient/settings/settings.component"; -import {PreferencesComponent} from "@app/shared/partials/preferences/preferences.component"; +import {Routes} from "@angular/router"; import {NodeResolver} from "@app/shared/resolvers/node.resolver"; import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; import {RTipsResolver} from "@app/shared/resolvers/r-tips-resolver.service"; -const routes: Routes = [ +export const recipientRoutes: Routes = [ { path: "", - component: HomeComponent, + loadComponent: () => import('@app/pages/recipient/home/home.component').then(m => m.HomeComponent), pathMatch: "full", data: {pageTitle: "Home"}, resolve: { @@ -20,7 +15,7 @@ const routes: Routes = [ }, { path: "home", - component: HomeComponent, + loadComponent: () => import('@app/pages/recipient/home/home.component').then(m => m.HomeComponent), pathMatch: "full", resolve: { PreferenceResolver, RTipsResolver @@ -29,7 +24,7 @@ const routes: Routes = [ }, { path: "reports", - component: TipsComponent, + loadComponent: () => import('@app/pages/recipient/tips/tips.component').then(m => m.TipsComponent), pathMatch: "full", resolve: { PreferenceResolver, RTipsResolver @@ -38,7 +33,7 @@ const routes: Routes = [ }, { path: "settings", - component: SettingsComponent, + loadComponent: () => import('@app/pages/recipient/settings/settings.component').then(m => m.RecipientSettingsComponent), resolve: { NodeResolver }, @@ -47,18 +42,11 @@ const routes: Routes = [ }, { path: "preferences", - component: PreferencesComponent, + loadComponent: () => import('@app/shared/partials/preferences/preferences.component').then(m => m.PreferencesComponent), pathMatch: "full", resolve: { PreferenceResolver, RTipsResolver }, data: {pageTitle: "Preferences"}, } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], -}) -export class RecipientRoutingModule { -} +]; \ No newline at end of file diff --git a/client/app/src/pages/recipient/settings/settings.component.html b/client/app/src/pages/recipient/settings/settings.component.html index b327d1dc18..c4cb25d47e 100644 --- a/client/app/src/pages/recipient/settings/settings.component.html +++ b/client/app/src/pages/recipient/settings/settings.component.html @@ -1,16 +1,16 @@
- -
- - - + +
+ + +
\ No newline at end of file diff --git a/client/app/src/pages/recipient/settings/settings.component.ts b/client/app/src/pages/recipient/settings/settings.component.ts index f0732367e4..1441f33c8b 100644 --- a/client/app/src/pages/recipient/settings/settings.component.ts +++ b/client/app/src/pages/recipient/settings/settings.component.ts @@ -1,19 +1,30 @@ -import {AfterViewInit, ChangeDetectorRef, Component, TemplateRef, ViewChild} from "@angular/core"; +import { AfterViewInit, ChangeDetectorRef, Component, TemplateRef, ViewChild, inject } from "@angular/core"; import {Router} from "@angular/router"; import {Tab} from "@app/models/component-model/tab"; import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; import {Tab1Component} from "@app/pages/admin/settings/tab1/tab1.component"; +import { FormsModule } from "@angular/forms"; +import { NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgbNavOutlet } from "@ng-bootstrap/ng-bootstrap"; +import { NgTemplateOutlet } from "@angular/common"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-recipient-settings", - templateUrl: "./settings.component.html" + selector: "src-recipient-settings", + templateUrl: "./settings.component.html", + standalone: true, + imports: [FormsModule, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgTemplateOutlet, NgbNavOutlet, Tab1Component, TranslateModule, TranslatorPipe] }) -export class SettingsComponent implements AfterViewInit { +export class RecipientSettingsComponent implements AfterViewInit { + private cdr = inject(ChangeDetectorRef); + private preferenceResolver = inject(PreferenceResolver); + private router = inject(Router); + @ViewChild("tab1") tab1!: TemplateRef; tabs: Tab[]; active: string; - constructor(private cdr: ChangeDetectorRef, private preferenceResolver: PreferenceResolver, private router: Router) { + constructor() { if (!this.preferenceResolver.dataModel.can_edit_general_settings) { this.router.navigate(['recipient/home']).then(); } diff --git a/client/app/src/pages/recipient/sidebar/sidebar.component.html b/client/app/src/pages/recipient/sidebar/sidebar.component.html index 8a41af5b2e..284299465a 100644 --- a/client/app/src/pages/recipient/sidebar/sidebar.component.html +++ b/client/app/src/pages/recipient/sidebar/sidebar.component.html @@ -1,12 +1,14 @@ - - {{'Home' | translate}} + + {{'Home' | translate}} - - {{'Reports' | translate}} + + {{'Reports' | translate}} - +@if (preferenceResolver.dataModel.can_edit_general_settings) { + {{'Settings' | translate}} - \ No newline at end of file + +} \ No newline at end of file diff --git a/client/app/src/pages/recipient/sidebar/sidebar.component.ts b/client/app/src/pages/recipient/sidebar/sidebar.component.ts index d2ed6a418f..163705d017 100644 --- a/client/app/src/pages/recipient/sidebar/sidebar.component.ts +++ b/client/app/src/pages/recipient/sidebar/sidebar.component.ts @@ -1,16 +1,21 @@ -import {Component} from "@angular/core"; -import {Router} from "@angular/router"; +import { Component, inject } from "@angular/core"; +import { Router, RouterLink, RouterLinkActive } from "@angular/router"; import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; +import { NgClass } from "@angular/common"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-receipt-sidebar", - templateUrl: "./sidebar.component.html" + selector: "src-receipt-sidebar", + templateUrl: "./sidebar.component.html", + standalone: true, + imports: [RouterLink, RouterLinkActive, NgClass, TranslateModule, TranslatorPipe] }) -export class SidebarComponent { - message: string; +export class ReceiptSidebarComponent { + private router = inject(Router); + protected preferenceResolver = inject(PreferenceResolver); - constructor(private router: Router, protected preferenceResolver: PreferenceResolver) { - } + message: string; isActive(route: string): boolean { return this.router.isActive(route, { diff --git a/client/app/src/pages/recipient/tip/tip.component.html b/client/app/src/pages/recipient/tip/tip.component.html index ec1c36e3e2..703feaa271 100644 --- a/client/app/src/pages/recipient/tip/tip.component.html +++ b/client/app/src/pages/recipient/tip/tip.component.html @@ -1,155 +1,175 @@ -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@if (tip) { +
+
+
+ + + + + + + + + + @if (tip && !tip.enable_notifications) { + + + + } + @if (!tip || tip.enable_notifications) { + + + + } + @if (preferencesService.dataModel.can_grant_access_to_reports) { + + + + } + @if (preferencesService.dataModel.can_grant_access_to_reports) { + + + + } + @if (preferencesService.dataModel.can_transfer_access_to_reports) { + + + + } + @if (!tip || tip.context) { + @if (!tip || preferencesService.dataModel.can_postpone_expiration && tip.context.tip_timetolive > 0) { + + -
+ } + } + @if (preferencesService.dataModel.can_delete_submission) { + + + + } + @if ((preferencesService.dataModel.can_redact_information || preferencesService.dataModel.can_mask_information)) { + + + + } + @if (tip.status !== 'closed') { + + + + } + @if (preferencesService.dataModel.can_reopen_reports && tip.status === 'closed') { + + + + } + + + + + + + + + +
-
- -
+
+ +
- -
-
- -
+ @if (tip && tip.receivers) { + @if (tip.receivers.length > 1) { +
+
+ +
- + } + }
-
- +
+ +
+ @if (tip && tip.enable_whistleblower_identity) { +
+
- -
- -
-
+ }
-
- -
+
+ +
- -
- - - - -
-
- -
- - - - -
-
- -
- - - - -
-
+ +
+ + @if (tip && tip.context) { + + } +
+
+ +
+ + @if (tip && tip.context) { + + } +
+
+ +
+ + @if (tip && tip.context) { + + } +
+
-
+
+} diff --git a/client/app/src/pages/recipient/tip/tip.component.ts b/client/app/src/pages/recipient/tip/tip.component.ts index 9d3a8f3371..94901181e9 100644 --- a/client/app/src/pages/recipient/tip/tip.component.ts +++ b/client/app/src/pages/recipient/tip/tip.component.ts @@ -1,8 +1,8 @@ -import {ChangeDetectorRef, Component, OnInit, TemplateRef, ViewChild} from "@angular/core"; +import { ChangeDetectorRef, Component, OnInit, TemplateRef, ViewChild, inject } from "@angular/core"; import {ActivatedRoute, Router} from "@angular/router"; import {AppConfigService} from "@app/services/root/app-config.service"; import {TipService} from "@app/shared/services/tip-service"; -import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; +import { NgbModal, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLinkButton, NgbNavLinkBase, NgbNavContent, NgbNavOutlet } from "@ng-bootstrap/ng-bootstrap"; import {AppDataService} from "@app/app-data.service"; import {ReceiverTipService} from "@app/services/helper/receiver-tip.service"; import {GrantAccessComponent} from "@app/shared/modals/grant-access/grant-access.component"; @@ -29,14 +29,60 @@ import {TipUploadWbFileComponent} from "@app/shared/partials/tip-upload-wbfile/t import {TipCommentsComponent} from "@app/shared/partials/tip-comments/tip-comments.component"; import {ReopenSubmissionComponent} from "@app/shared/modals/reopen-submission/reopen-submission.component"; import {ChangeSubmissionStatusComponent} from "@app/shared/modals/change-submission-status/change-submission-status.component"; -import {TranslateService} from "@ngx-translate/core"; +import { TranslateService, TranslateModule } from "@ngx-translate/core"; +import { NgClass, NgTemplateOutlet } from "@angular/common"; +import { TipInfoComponent } from "../../../shared/partials/tip-info/tip-info.component"; +import { TipReceiverListComponent } from "../../../shared/partials/tip-receiver-list/tip-receiver-list.component"; +import { TipQuestionnaireAnswersComponent } from "../../../shared/partials/tip-questionnaire-answers/tip-questionnaire-answers.component"; +import { WhistleBlowerIdentityReceiverComponent } from "../whistleblower-identity-reciever/whistle-blower-identity-receiver.component"; +import { TipFilesReceiverComponent } from "../../../shared/partials/tip-files-receiver/tip-files-receiver.component"; +import { TipUploadWbFileComponent as TipUploadWbFileComponent_1 } from "../../../shared/partials/tip-upload-wbfile/tip-upload-wb-file.component"; +import { TipCommentsComponent as TipCommentsComponent_1 } from "../../../shared/partials/tip-comments/tip-comments.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-tip", - templateUrl: "./tip.component.html", + selector: "src-tip", + templateUrl: "./tip.component.html", + standalone: true, + imports: [ + NgClass, + TipInfoComponent, + TipReceiverListComponent, + TipQuestionnaireAnswersComponent, + WhistleBlowerIdentityReceiverComponent, + TipFilesReceiverComponent, + NgbNav, + NgbNavItem, + NgbNavItemRole, + NgbNavLinkButton, + NgbNavLinkBase, + NgbNavContent, + NgTemplateOutlet, + NgbNavOutlet, + TipUploadWbFileComponent_1, + TipCommentsComponent_1, + TranslateModule, + TranslatorPipe +], }) export class TipComponent implements OnInit { + private translateService = inject(TranslateService); + private tipService = inject(TipService); + private appConfigServices = inject(AppConfigService); + private router = inject(Router); + private cdr = inject(ChangeDetectorRef); + private cryptoService = inject(CryptoService); + protected utils = inject(UtilsService); + protected preferencesService = inject(PreferenceResolver); + protected modalService = inject(NgbModal); + private activatedRoute = inject(ActivatedRoute); + protected httpService = inject(HttpService); + protected http = inject(HttpClient); + protected appDataService = inject(AppDataService); + protected RTipService = inject(ReceiverTipService); + protected authenticationService = inject(AuthenticationService); + @ViewChild("tab1") tab1!: TemplateRef; @ViewChild("tab2") tab2!: TemplateRef; @ViewChild("tab3") tab3!: TemplateRef; @@ -52,9 +98,6 @@ export class TipComponent implements OnInit { redactOperationTitle: string; tabs: Tab[]; - constructor(private translateService: TranslateService,private tipService: TipService, private appConfigServices: AppConfigService, private router: Router, private cdr: ChangeDetectorRef, private cryptoService: CryptoService, protected utils: UtilsService, protected preferencesService: PreferenceResolver, protected modalService: NgbModal, private activatedRoute: ActivatedRoute, protected httpService: HttpService, protected http: HttpClient, protected appDataService: AppDataService, protected RTipService: ReceiverTipService, protected authenticationService: AuthenticationService) { - } - ngOnInit() { this.loadTipDate(); this.cdr.detectChanges(); @@ -253,7 +296,7 @@ export class TipComponent implements OnInit { for (const y of x.substatuses) { output.push({ id: `${x.id}:${y.id}`, - label: (x.label ? this.translateService.instant(x.label) : '') + ' \u2013 ' + y.label, + label: this.translateService.instant(x.label) + ' \u2013 ' + y.label, status: x.id, substatus: y.id, order: output.length, diff --git a/client/app/src/pages/recipient/tips/tips.component.html b/client/app/src/pages/recipient/tips/tips.component.html index 02018b93eb..5b133b051f 100644 --- a/client/app/src/pages/recipient/tips/tips.component.html +++ b/client/app/src/pages/recipient/tips/tips.component.html @@ -1,277 +1,405 @@ - -
-
- - - - - +@if (RTips.dataModel && RTips.dataModel && filteredTips) { +
+
+ + + + @if (selectedTips.length !== RTips.dataModel.length ) { + + + + } + @if (selectedTips.length === RTips.dataModel.length) { + + + + } + @if (selectedTips.length) { + + @if (preference.dataModel.can_grant_access_to_reports) { + + - - + } + @if (preference.dataModel.can_grant_access_to_reports) { + + - - - + } + + + + + } + + + + + + + + + +
+ +
+
+
+
+
+
+
+ + + + + + + + @if (appDataService.public.contexts.length > 1) { + + } + + + + + + + + + + + @if (appDataService.public.node.enable_scoring_system) { + + } + + + + @for (tip of orderbyCast(filteredTips | orderBy:sortKey:sortReverse) | slice:((currentPage - 1) * itemsPerPage):((currentPage - 1) * itemsPerPage + itemsPerPage); let index = $index; track tip) { + + + + + @if (appDataService.public.contexts.length > 1) { + + } + + + + + + + + + + + @if (appDataService.public.node.enable_scoring_system) { + + } + + } + +
+ + @if (sortKey === 'important') { + + @if (!sortReverse) { + + } + @if (sortReverse) { + + } - - + } + + + @if (sortKey === 'reminder_date') { + + @if (!sortReverse) { + + } + @if (sortReverse) { + + } - - + } + + + @if (sortKey === 'progressive') { + + @if (!sortReverse) { + + } + @if (sortReverse) { + + } - - - - - - - - - - -
- -
+ } +
+ + + {{'Channel' | translate}} + @if (sortKey === 'context_name') { + + @if (!sortReverse) { + + } + @if (sortReverse) { + + } + + } - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - {{'Channel' | translate}} - - - - - - - - - - - - - - - {{'Label' | translate}} - - - - - - - - - {{'Status' | translate}} - - - - - - - - - - - - - - - {{'Report date' | translate}} - - - - - - - -
- -
-
-
- - - {{'Last update' | translate}} - - - - - - - -
- -
-
-
- - - {{'Expiration date' | translate}} - - - - - - - -
- -
-
-
- - - - - - - - - - - - - - - - - - - - {{'Score' | translate}} - - - - - - - - - - - -
- - - - - - - - - - - {{tip.progressive}}{{tip.context_name}}{{tip.label}}{{tip.submissionStatusStr}}{{tip.creation_date | date:'dd-MM-yyyy HH:mm'}}{{tip.update_date | date:'dd-MM-yyyy HH:mm'}} - {{tip.expiration_date | date:'dd-MM-yyyy HH:mm'}} - - - - - - - {{tip.comment_count}}{{tip.file_count}} - - - - - - - - - - - {{tip.receiver_count}} - - - - {{ 'Low' | translate }} - {{ 'Medium' | translate }} - {{ 'High' | translate }} - -
-
- - - - - - -
- + @if (RTips.dataModel.length > 0) { + + + + + + + } +
+ + + {{'Label' | translate}} + @if (sortKey === 'label') { + + @if (!sortReverse) { + + } + @if (sortReverse) { + + } + + } + + + + + {{'Status' | translate}} + @if (sortKey === 'submissionStatusStr') { + + @if (!sortReverse) { + + } + @if (sortReverse) { + + } + + } + + @if (RTips.dataModel.length > 0) { + + + + + + + } + + + + {{'Report date' | translate}} + @if (sortKey === 'creation_date') { + + @if (!sortReverse) { + + } + @if (sortReverse) { + + } + + } + + + +
+ +
+
+
+ + + {{'Last update' | translate}} + @if (sortKey === 'update_date') { + + @if (!sortReverse) { + + } + @if (sortReverse) { + + } + + } + + + +
+ +
+
+
+ + + {{'Expiration date' | translate}} + @if (sortKey === 'expiration_date') { + + @if (!sortReverse) { + + } + @if (sortReverse) { + + } + + } + + + +
+ +
+
+
+ + + + + + + + + + + + @if (sortKey === 'receiver_count') { + + @if (!sortReverse) { + + } + @if (sortReverse) { + + } + + } + + + + + {{'Score' | translate}} + @if (sortKey === 'score') { + + @if (!sortReverse) { + + } + @if (sortReverse) { + + } + + } + + @if (RTips.dataModel.length > 0) { + + + + + + + } +
+ @if (isSelected(tip.id)) { + + + + } + @if (!isSelected(tip.id)) { + + + + } + + @if (tip.important) { + + } + + @if (!utils.isNever(tip.reminder_date)) { + + } + {{tip.progressive}}{{tip.context_name}}{{tip.label}}{{tip.submissionStatusStr}}{{tip.creation_date | date:'dd-MM-yyyy HH:mm'}}{{tip.update_date | date:'dd-MM-yyyy HH:mm'}} + @if (!utils.isNever(tip.expiration_date)) { + {{tip.expiration_date | date:'dd-MM-yyyy HH:mm'}} + } + @if (utils.isNever(tip.expiration_date)) { + - + } + + @if (tip.last_access >= tip.update_date) { + + } + @if (tip.last_access < tip.update_date) { + + + } + {{tip.comment_count}}{{tip.file_count}} + @switch (tip.subscription) { + @case (0) { + + } + @case (1) { + + } + @case (2) { + @if (tip.data) { + + } + @if (!tip.data) { + + } + } + } + {{tip.receiver_count}} + @switch (tip.score) { + @case (0) { + - + } + @case (1) { + {{ 'Low' | translate }} + } + @case (2) { + {{ 'Medium' | translate }} + } + @case (3) { + {{ 'High' | translate }} + } + } +
+ @if (filteredTips.length > itemsPerPage) { +
+ + < {{ 'Previous' | translate }} + + + {{ 'Next' | translate }} > + + << {{ 'First' | translate }} + + + {{ 'Last' | translate }} >> + + +
+ } +
+ @if (filteredTips.length > 0) { + + + {{ 'Export' | translate }} + + }
+
- + } diff --git a/client/app/src/pages/recipient/tips/tips.component.ts b/client/app/src/pages/recipient/tips/tips.component.ts index dc74f678e2..a965539b19 100644 --- a/client/app/src/pages/recipient/tips/tips.component.ts +++ b/client/app/src/pages/recipient/tips/tips.component.ts @@ -1,30 +1,49 @@ -import {Component, HostListener, OnInit} from "@angular/core"; +import { Component, HostListener, OnInit, inject } from "@angular/core"; import {AppConfigService} from "@app/services/root/app-config.service"; -import {NgbDate, NgbModal} from "@ng-bootstrap/ng-bootstrap"; +import { NgbDate, NgbModal, NgbPagination, NgbPaginationPrevious, NgbPaginationNext, NgbPaginationFirst, NgbPaginationLast } from "@ng-bootstrap/ng-bootstrap"; import {AppDataService} from "@app/app-data.service"; import {GrantAccessComponent} from "@app/shared/modals/grant-access/grant-access.component"; import {RevokeAccessComponent} from "@app/shared/modals/revoke-access/revoke-access.component"; import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; import {RTipsResolver} from "@app/shared/resolvers/r-tips-resolver.service"; import {UtilsService} from "@app/shared/services/utils.service"; -import {TranslateService} from "@ngx-translate/core"; -import {IDropdownSettings} from "ng-multiselect-dropdown"; +import { TranslateService, TranslateModule } from "@ngx-translate/core"; +import { IDropdownSettings, NgMultiSelectDropDownModule } from "ng-multiselect-dropdown"; import {filter, orderBy} from "lodash-es"; import {TokenResource} from "@app/shared/services/token-resource.service"; -import {Router} from "@angular/router"; +import { Router, RouterLink } from "@angular/router"; import {rtipResolverModel} from "@app/models/resolvers/rtips-resolver-model"; import {Receiver} from "@app/models/reciever/reciever-tip-data"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {HttpService} from "@app/shared/services/http.service"; import {Observable, from, switchMap} from "rxjs"; import {HttpClient, HttpResponse} from "@angular/common/http"; -import {formatDate} from "@angular/common"; +import { formatDate, NgClass, SlicePipe, DatePipe } from "@angular/common"; +import { FormsModule } from "@angular/forms"; +import { DateRangeSelectorComponent } from "../../../shared/components/date-selector/date-selector.component"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { OrderByPipe } from "@app/shared/pipes/order-by.pipe"; @Component({ - selector: "src-tips", - templateUrl: "./tips.component.html" + selector: "src-tips", + templateUrl: "./tips.component.html", + standalone: true, + imports: [RouterLink, FormsModule, NgClass, NgMultiSelectDropDownModule, DateRangeSelectorComponent, NgbPagination, NgbPaginationPrevious, NgbPaginationNext, NgbPaginationFirst, NgbPaginationLast, SlicePipe, DatePipe, TranslateModule, TranslatorPipe, OrderByPipe] }) export class TipsComponent implements OnInit { + private http = inject(HttpClient); + protected authenticationService = inject(AuthenticationService); + protected httpService = inject(HttpService); + private appConfigServices = inject(AppConfigService); + private router = inject(Router); + protected RTips = inject(RTipsResolver); + protected preference = inject(PreferenceResolver); + private modalService = inject(NgbModal); + protected utils = inject(UtilsService); + protected appDataService = inject(AppDataService); + private translateService = inject(TranslateService); + private tokenResourceService = inject(TokenResource); + search: string | undefined; selectedTips: string[] = []; filteredTips: rtipResolverModel[]; @@ -62,10 +81,6 @@ export class TipsComponent implements OnInit { searchPlaceholderText: this.translateService.instant("Search") }; - constructor(private http: HttpClient,protected authenticationService: AuthenticationService, protected httpService: HttpService, private appConfigServices: AppConfigService, private router: Router, protected RTips: RTipsResolver, protected preference: PreferenceResolver, private modalService: NgbModal, protected utils: UtilsService, protected appDataService: AppDataService, private translateService: TranslateService, private tokenResourceService: TokenResource) { - - } - ngOnInit() { if (!this.RTips.dataModel) { this.router.navigate(["/recipient/home"]).then(); @@ -428,6 +443,6 @@ export class TipsComponent implements OnInit { 'Number of Files', 'Subscription', 'Number of Recipients' - ].map(header => header ? this.translateService.instant(header) : ''); + ].map(header => this.translateService.instant(header)); } } diff --git a/client/app/src/pages/recipient/whistleblower-identity-reciever/whistle-blower-identity-receiver.component.html b/client/app/src/pages/recipient/whistleblower-identity-reciever/whistle-blower-identity-receiver.component.html index 1b526c7413..6d0a0874fe 100644 --- a/client/app/src/pages/recipient/whistleblower-identity-reciever/whistle-blower-identity-receiver.component.html +++ b/client/app/src/pages/recipient/whistleblower-identity-reciever/whistle-blower-identity-receiver.component.html @@ -1,48 +1,80 @@
-
- {{'Identity' | translate}} - - - - -
-
-
-
- -
-
- - : - {{tipService.tip.iar.request_date | date:'dd-MM-yyyy HH:mm'}} -
+
+ {{'Identity' | translate}} + + @if (!collapsed) { + + } + @if (collapsed) { + + } + +
+ @if (!collapsed) { +
+ @if (tipService.tip.iar && tipService.tip.iar.reply !== 'authorized') { +
+
+ +
+
+ + : + {{tipService.tip.iar.request_date | date:'dd-MM-yyyy HH:mm'}} +
+
+ + : + + @switch (tipService.tip.iar.reply) { + @case ('pending') { + {{ 'Waiting for authorization' | translate }} + } + @case ('denied') { + {{ 'Denied' | translate }} + } + } + +
: {{tipService.tip.iar.request_motivation}}
+
+ @if (tipService.tip.iar && tipService.tip.iar.reply === 'denied') {
- - : - - {{ 'Waiting for authorization' | translate }} - {{ 'Denied' | translate }} - -
: {{tipService.tip.iar.request_motivation}}
-
-
- : {{tipService.tip.iar.reply_motivation}} + : {{tipService.tip.iar.reply_motivation}}
+ }
- - -
-
-
- + } + @if (!tipService.tip.data.whistleblower_identity_provided) { + - + } + @if (tipService.tip.data.whistleblower_identity_provided) { +
+ @if (tipService.tip.iar && tipService.tip.iar.reply === 'authorized') { +
+ @for (field of tipService.tip.whistleblower_identity_field.children ; track field; let index = $index) { +
+
+ }
-
- - + } + @if (!tipService.tip.iar || tipService.tip.iar.reply === 'denied') { +
+ @if (tipService.tip.custodian) { + + } + @if (!tipService.tip.custodian) { + + }
-
- {{ 'Subscription date'|translate }} : {{tipService.tip.data.whistleblower_identity_date | date:'dd-MM-yyyy HH:mm'}} + } + @if (tipService.tip.data.whistleblower_identity_date !== tipService.tip.creation_date) { +
+ {{ 'Subscription date'|translate }} : {{tipService.tip.data.whistleblower_identity_date | date:'dd-MM-yyyy HH:mm'}}
+ }
+ }
+ }
\ No newline at end of file diff --git a/client/app/src/pages/recipient/whistleblower-identity-reciever/whistle-blower-identity-receiver.component.ts b/client/app/src/pages/recipient/whistleblower-identity-reciever/whistle-blower-identity-receiver.component.ts index 3495750258..bae00ef397 100644 --- a/client/app/src/pages/recipient/whistleblower-identity-reciever/whistle-blower-identity-receiver.component.ts +++ b/client/app/src/pages/recipient/whistleblower-identity-reciever/whistle-blower-identity-receiver.component.ts @@ -1,4 +1,4 @@ -import {Component, Input} from "@angular/core"; +import { Component, Input, inject } from "@angular/core"; import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; import {ReceiverTipService} from "@app/services/helper/receiver-tip.service"; import { @@ -6,20 +6,29 @@ import { } from "@app/shared/modals/tip-operation-file-identity-access-request/tip-operation-file-identity-access-request.ompoent"; import {HttpService} from "@app/shared/services/http.service"; import {UtilsService} from "@app/shared/services/utils.service"; +import { NgClass, DatePipe } from "@angular/common"; +import { TipFieldComponent } from "../../../shared/partials/tip-field/tip-field.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-whistleblower-identity-reciever", - templateUrl: "./whistle-blower-identity-receiver.component.html" + selector: "src-whistleblower-identity-reciever", + templateUrl: "./whistle-blower-identity-receiver.component.html", + standalone: true, + imports: [TipFieldComponent, NgClass, DatePipe, TranslateModule, TranslatorPipe] }) export class WhistleBlowerIdentityReceiverComponent { + protected tipService = inject(ReceiverTipService); + protected utilsService = inject(UtilsService); + private httpService = inject(HttpService); + private modalService = inject(NgbModal); + private utils = inject(UtilsService); + @Input() redactOperationTitle: string; @Input() redactMode: boolean; collapsed: boolean = true; - constructor(protected tipService: ReceiverTipService, protected utilsService: UtilsService, private httpService: HttpService, private modalService: NgbModal, private utils: UtilsService) { - } - public toggleCollapse() { this.collapsed = !this.collapsed; } diff --git a/client/app/src/pages/signup/signup-routing.module.ts b/client/app/src/pages/signup/signup-routing.module.ts deleted file mode 100644 index f6f85820b9..0000000000 --- a/client/app/src/pages/signup/signup-routing.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {SignupComponent} from "@app/pages/signup/signup/signup.component"; - -const routes: Routes = [ - { - path: "", - component: SignupComponent, - pathMatch: "full", - }, -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], -}) -export class SignupRoutingModule { -} diff --git a/client/app/src/pages/signup/signup.module.ts b/client/app/src/pages/signup/signup.module.ts deleted file mode 100644 index b962de3089..0000000000 --- a/client/app/src/pages/signup/signup.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {NgModule} from "@angular/core"; -import {MarkdownModule} from "ngx-markdown"; -import {CommonModule} from "@angular/common"; -import {SignupComponent} from "@app/pages/signup/signup/signup.component"; -import {TranslateModule} from "@ngx-translate/core"; -import {SignupdefaultComponent} from "@app/pages/signup/templates/signupdefault/signupdefault.component"; -import {SharedModule} from "@app/shared.module"; -import {FormsModule, ReactiveFormsModule} from "@angular/forms"; -import {NgSelectModule} from "@ng-select/ng-select"; -import {TosComponent} from "@app/pages/signup/templates/tos/tos.component"; -import {WbpaComponent} from "@app/pages/signup/templates/wbpa/wbpa.component"; -import {ActivationComponent} from "@app/pages/signup/templates/activation/activation.component"; - - -@NgModule({ - declarations: [ - SignupComponent, - SignupdefaultComponent, - TosComponent, - WbpaComponent, - ActivationComponent - ], - imports: [ - CommonModule, - MarkdownModule, - TranslateModule, - FormsModule, - ReactiveFormsModule, - NgSelectModule, - SharedModule - ] -}) -export class SignupModule { -} diff --git a/client/app/src/pages/signup/signup.routes.ts b/client/app/src/pages/signup/signup.routes.ts new file mode 100644 index 0000000000..c2e565f5b5 --- /dev/null +++ b/client/app/src/pages/signup/signup.routes.ts @@ -0,0 +1,9 @@ +import {Routes} from "@angular/router"; + +export const signupRoutes: Routes = [ + { + path: "", + loadComponent: () => import('@app/pages/signup/signup/signup.component').then(m => m.SignupComponent), + pathMatch: "full", + }, +]; \ No newline at end of file diff --git a/client/app/src/pages/signup/signup/signup.component.html b/client/app/src/pages/signup/signup/signup.component.html index f693bd5707..f07b6a44c5 100644 --- a/client/app/src/pages/signup/signup/signup.component.html +++ b/client/app/src/pages/signup/signup/signup.component.html @@ -1,24 +1,36 @@
-
-
+ @if (step === 1) { +
+ @switch (appDataService.public.node.mode) { + @case ('default') { +
-
-
+
+ } + @case ('demo') { +
-
-
+
+ } + @case ('wbpa') { +
-
+
+ } + }
-
-
-
{{'Success!'|translate}}
-
- {{'Your whistleblowing platform is almost ready!'|translate}} - {{'Check your inbox to activate it.'|translate}} -
- {{'If not activated within 24 hours the platform will be automatically deleted.'|translate}} -
+ } + @if (step === 2) { +
+
+
{{'Success!'|translate}}
+
+ {{'Your whistleblowing platform is almost ready!'|translate}} + {{'Check your inbox to activate it.'|translate}} +
+ {{'If not activated within 24 hours the platform will be automatically deleted.'|translate}}
+
+ }
\ No newline at end of file diff --git a/client/app/src/pages/signup/signup/signup.component.ts b/client/app/src/pages/signup/signup/signup.component.ts index 508a8e1486..94147f3dd1 100644 --- a/client/app/src/pages/signup/signup/signup.component.ts +++ b/client/app/src/pages/signup/signup/signup.component.ts @@ -1,14 +1,25 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {AppDataService} from "@app/app-data.service"; import {HttpService} from "@app/shared/services/http.service"; import {AppConfigService} from "@app/services/root/app-config.service"; import {Signup} from "@app/models/component-model/signup"; +import { SignupdefaultComponent } from "../templates/signupdefault/signupdefault.component"; +import { WbpaComponent } from "../templates/wbpa/wbpa.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; + @Component({ - selector: "src-signup", - templateUrl: "./signup.component.html" + selector: "src-signup", + templateUrl: "./signup.component.html", + standalone: true, + imports: [SignupdefaultComponent, WbpaComponent, TranslateModule, TranslatorPipe] }) export class SignupComponent implements OnInit { + protected appDataService = inject(AppDataService); + private httpService = inject(HttpService); + private appConfig = inject(AppConfigService); + hostname = ""; completed = false; step = 1; @@ -28,9 +39,6 @@ export class SignupComponent implements OnInit { "tos2": false }; - constructor(protected appDataService: AppDataService, private httpService: HttpService, private appConfig: AppConfigService) { - } - ngOnInit() { this.appConfig.routeChangeListener(); } diff --git a/client/app/src/pages/signup/templates/activation/activation.component.ts b/client/app/src/pages/signup/templates/activation/activation.component.ts index 5b7a452b24..a56427256e 100644 --- a/client/app/src/pages/signup/templates/activation/activation.component.ts +++ b/client/app/src/pages/signup/templates/activation/activation.component.ts @@ -1,15 +1,19 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {ActivatedRoute} from "@angular/router"; import {HttpService} from "@app/shared/services/http.service"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-activation", - templateUrl: "./activation.component.html" + selector: "src-activation", + templateUrl: "./activation.component.html", + standalone: true, + imports: [TranslateModule, TranslatorPipe] }) export class ActivationComponent implements OnInit { + private route = inject(ActivatedRoute); + private httpService = inject(HttpService); - constructor(private route: ActivatedRoute, private httpService: HttpService) { - } ngOnInit(): void { this.route.queryParams.subscribe(params => { diff --git a/client/app/src/pages/signup/templates/signupdefault/signupdefault.component.html b/client/app/src/pages/signup/templates/signupdefault/signupdefault.component.html index f62669564c..ed40898f71 100644 --- a/client/app/src/pages/signup/templates/signupdefault/signupdefault.component.html +++ b/client/app/src/pages/signup/templates/signupdefault/signupdefault.component.html @@ -1,64 +1,68 @@
-
-
-
{{'Sign up'|translate}}
-
+
+
+
{{'Sign up'|translate}}
-
-
- -
- - - .{{appDataService.public.node.rootdomain}} - -
-
+
+
+
+ +
+ + + .{{appDataService.public.node.rootdomain}} + +
-
-
- - * - -
-
- - * - -
+
+
+
+ + * +
-
-
- - -
- {{'Invalid email address'|translate}} -
-
-
- - -
- {{'Invalid confirmation'|translate}} -
+
+ + * + +
+
+
+
+ + + @if (validated && mail_address?.errors?.['pattern']) { +
+ {{'Invalid email address'|translate}}
+ }
- -
-
- +
+ + + @if (validated && signupform && mail_address.valid && signup.email !== confirmation_email) { +
+ {{'Invalid confirmation'|translate}}
+ } +
+
+ +
+
+
+
diff --git a/client/app/src/pages/signup/templates/signupdefault/signupdefault.component.ts b/client/app/src/pages/signup/templates/signupdefault/signupdefault.component.ts index b0ef7d895d..53a737dd9c 100644 --- a/client/app/src/pages/signup/templates/signupdefault/signupdefault.component.ts +++ b/client/app/src/pages/signup/templates/signupdefault/signupdefault.component.ts @@ -1,13 +1,24 @@ -import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core"; +import { Component, EventEmitter, Input, OnInit, Output, inject } from "@angular/core"; import {AppDataService} from "@app/app-data.service"; import {Signup} from "@app/models/component-model/signup"; import * as Constants from "@app/shared/constants/constants"; +import { FormsModule } from "@angular/forms"; +import { NgClass } from "@angular/common"; +import { SubdomainValidatorDirective } from "../../../../shared/directive/subdomain-validator.directive"; +import { DisableCcpDirective } from "../../../../shared/directive/disable-ccp.directive"; +import { TosComponent } from "../tos/tos.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-signupdefault", - templateUrl: "./signupdefault.component.html" + selector: "src-signupdefault", + templateUrl: "./signupdefault.component.html", + standalone: true, + imports: [FormsModule, NgClass, SubdomainValidatorDirective, DisableCcpDirective, TosComponent, TranslateModule, TranslatorPipe] }) export class SignupdefaultComponent implements OnInit { + protected appDataService = inject(AppDataService); + @Input() signup: Signup; @Output() complete: EventEmitter = new EventEmitter(); @@ -17,9 +28,6 @@ export class SignupdefaultComponent implements OnInit { validated = false; mail: string; - constructor(protected appDataService: AppDataService) { - } - ngOnInit(): void { this.emailRegex = Constants.Constants.emailRegexp; } diff --git a/client/app/src/pages/signup/templates/tos/tos.component.html b/client/app/src/pages/signup/templates/tos/tos.component.html index 9ee1ea70bb..ceb19629e4 100644 --- a/client/app/src/pages/signup/templates/tos/tos.component.html +++ b/client/app/src/pages/signup/templates/tos/tos.component.html @@ -1,20 +1,24 @@
-
- -
{{appDataService.public.node.signup_tos1_text}}
-
- - -
+ @if (appDataService.public.node.signup_tos1_enable) { +
+ +
{{appDataService.public.node.signup_tos1_text}}
+
+ + +
+ }
-
- -
-
- - -
+ @if (appDataService.public.node.signup_tos2_enable) { +
+ +
+
+ + +
+ }
diff --git a/client/app/src/pages/signup/templates/tos/tos.component.ts b/client/app/src/pages/signup/templates/tos/tos.component.ts index dbf78e7b46..3d9f7d93b1 100644 --- a/client/app/src/pages/signup/templates/tos/tos.component.ts +++ b/client/app/src/pages/signup/templates/tos/tos.component.ts @@ -1,17 +1,21 @@ -import {Component, Input} from "@angular/core"; +import { Component, Input, inject } from "@angular/core"; import {AppDataService} from "@app/app-data.service"; -import {ControlContainer, NgForm} from "@angular/forms"; +import { ControlContainer, NgForm, FormsModule } from "@angular/forms"; import {Signup} from "@app/models/component-model/signup"; +import { MarkdownComponent } from "ngx-markdown"; +import { StripHtmlPipe } from "@app/shared/pipes/strip-html.pipe"; + @Component({ - selector: "src-tos", - templateUrl: "./tos.component.html", - viewProviders: [{provide: ControlContainer, useExisting: NgForm}] + selector: "src-tos", + templateUrl: "./tos.component.html", + viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], + standalone: true, + imports: [FormsModule, MarkdownComponent, StripHtmlPipe] }) export class TosComponent { + protected appDataService = inject(AppDataService); + @Input() signup: Signup; @Input() signupform: NgForm; - - constructor(protected appDataService: AppDataService) { - } } diff --git a/client/app/src/pages/signup/templates/wbpa/wbpa.component.html b/client/app/src/pages/signup/templates/wbpa/wbpa.component.html index 3b2a56a871..fc85b4cdc1 100644 --- a/client/app/src/pages/signup/templates/wbpa/wbpa.component.html +++ b/client/app/src/pages/signup/templates/wbpa/wbpa.component.html @@ -1,109 +1,115 @@
-
-
-
{{'Sign up'|translate}}
-
+
+
+
{{'Sign up'|translate}}
-
-
- - * - -
-
- - * - -
+
+
+
+ + * +
-
-
- - * - -
- {{'Invalid phone number'|translate}} -
-
+
+ + * +
-
-
- -
- -
- {{'Invalid email address'|translate}} -
-
-
-
- -
- -
- {{'Invalid confirmation'|translate}} -
-
+
+
+
+ + * + + @if (phone?.errors?.['pattern']) { +
+ {{'Invalid phone number'|translate}}
+ }
-
-
- -
+
+
+
+ +
+ + @if (email?.errors?.['pattern']) { +
+ {{'Invalid email address'|translate}} +
+ } +
-
-
- - * - -
+
+ +
+ + @if (validated && signupform && email.valid && signup.email !== confirmation_email) { +
+ {{'Invalid confirmation'|translate}} +
+ } +
-
-
- - * - -
+
+
+
+
-
-
- - * - -
-
- - * - -
+
+
+
+ + * +
-
-
- -
- - - .{{appDataService.public.node.rootdomain}} - -
-
+
+
+
+ + * +
- -
-
- -
+
+
+
+ + * + +
+
+ + * + +
+
+
+
+ +
+ + + .{{appDataService.public.node.rootdomain}} + +
+
+
+ +
+
+
+
diff --git a/client/app/src/pages/signup/templates/wbpa/wbpa.component.ts b/client/app/src/pages/signup/templates/wbpa/wbpa.component.ts index 3b60d1a02c..be78223ed6 100644 --- a/client/app/src/pages/signup/templates/wbpa/wbpa.component.ts +++ b/client/app/src/pages/signup/templates/wbpa/wbpa.component.ts @@ -1,13 +1,24 @@ -import {Component, EventEmitter, Input, Output} from "@angular/core"; +import { Component, EventEmitter, Input, Output, inject } from "@angular/core"; import * as Constants from "@app/shared/constants/constants"; import {AppDataService} from "@app/app-data.service"; import {Signup} from "@app/models/component-model/signup"; +import { FormsModule } from "@angular/forms"; +import { NgClass } from "@angular/common"; +import { DisableCcpDirective } from "../../../../shared/directive/disable-ccp.directive"; +import { SubdomainValidatorDirective } from "../../../../shared/directive/subdomain-validator.directive"; +import { TosComponent } from "../tos/tos.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-wbpa", - templateUrl: "./wbpa.component.html" + selector: "src-wbpa", + templateUrl: "./wbpa.component.html", + standalone: true, + imports: [FormsModule, NgClass, DisableCcpDirective, SubdomainValidatorDirective, TosComponent, TranslateModule, TranslatorPipe] }) export class WbpaComponent { + protected appDataService = inject(AppDataService); + @Input() signup: Signup; @Output() complete: EventEmitter = new EventEmitter(); @Output() updateSubdomain: EventEmitter = new EventEmitter(); @@ -15,6 +26,4 @@ export class WbpaComponent { protected readonly Constants = Constants; validated = false; confirmation_email: string; - constructor(protected appDataService: AppDataService) { - } } diff --git a/client/app/src/pages/whistleblower/context-selection/context-selection.component.html b/client/app/src/pages/whistleblower/context-selection/context-selection.component.html index 0d23982fcd..8c5cbd54a9 100644 --- a/client/app/src/pages/whistleblower/context-selection/context-selection.component.html +++ b/client/app/src/pages/whistleblower/context-selection/context-selection.component.html @@ -1,22 +1,32 @@ -
+@if (appDataService.public.node.contexts_clarification) { +
- + -
+
+}
-
-
-
-
{{context.name}}
-
-
-
- context picture -
-
-
-
{{context.description}}
-
+ @for (context of (selectable_contexts| orderBy:contextsOrderPredicate); track context; let index = $index) { +
+
+
+
{{context.name}}
+ @if (context.picture) { +
+
+ @if (context.picture) { + context picture + } +
+
+ } + @if (context.description) { +
+
{{context.description}}
+
+ } +
+ }
\ No newline at end of file diff --git a/client/app/src/pages/whistleblower/context-selection/context-selection.component.ts b/client/app/src/pages/whistleblower/context-selection/context-selection.component.ts index 78bdc5568d..8a137d9ac9 100644 --- a/client/app/src/pages/whistleblower/context-selection/context-selection.component.ts +++ b/client/app/src/pages/whistleblower/context-selection/context-selection.component.ts @@ -1,18 +1,24 @@ -import {Component, EventEmitter, Input, Output} from "@angular/core"; +import { Component, EventEmitter, Input, Output, inject } from "@angular/core"; import {AppDataService} from "@app/app-data.service"; import {Context} from "@app/models/app/public-model"; import {UtilsService} from "@app/shared/services/utils.service"; +import { NgOptimizedImage } from "@angular/common"; +import { MarkdownComponent } from "ngx-markdown"; +import { StripHtmlPipe } from "@app/shared/pipes/strip-html.pipe"; +import { OrderByPipe } from "@app/shared/pipes/order-by.pipe"; @Component({ - selector: "src-context-selection", - templateUrl: "./context-selection.component.html" + selector: "src-context-selection", + templateUrl: "./context-selection.component.html", + standalone: true, + imports: [MarkdownComponent, NgOptimizedImage, StripHtmlPipe, OrderByPipe] }) export class ContextSelectionComponent { + protected appDataService = inject(AppDataService); + protected utilsService = inject(UtilsService); + @Input() selectable_contexts: Context[]; @Input() contextsOrderPredicate: string; @Output() selectContext: EventEmitter = new EventEmitter(); - - constructor(protected appDataService: AppDataService, protected utilsService: UtilsService) { - } } diff --git a/client/app/src/pages/whistleblower/fields/whistleblower-identity-field/whistleblower-identity-field.component.html b/client/app/src/pages/whistleblower/fields/whistleblower-identity-field/whistleblower-identity-field.component.html index 8763de6bc2..c1a548ea59 100644 --- a/client/app/src/pages/whistleblower/fields/whistleblower-identity-field/whistleblower-identity-field.component.html +++ b/client/app/src/pages/whistleblower/fields/whistleblower-identity-field/whistleblower-identity-field.component.html @@ -1,14 +1,20 @@
-
- - - - + @if (!field.required) { +
+ + + +
-
-
{{field.attrs.text_shown_upon_negative_answer.value|translate}}
+ } + @if (!identity_provided && field.attrs.text_shown_upon_negative_answer.value) { +
+
{{field.attrs.text_shown_upon_negative_answer.value|translate}}
-
- + } + @if (identity_provided) { +
+
+ }
\ No newline at end of file diff --git a/client/app/src/pages/whistleblower/fields/whistleblower-identity-field/whistleblower-identity-field.component.ts b/client/app/src/pages/whistleblower/fields/whistleblower-identity-field/whistleblower-identity-field.component.ts index de53530ffb..334f408327 100644 --- a/client/app/src/pages/whistleblower/fields/whistleblower-identity-field/whistleblower-identity-field.component.ts +++ b/client/app/src/pages/whistleblower/fields/whistleblower-identity-field/whistleblower-identity-field.component.ts @@ -1,14 +1,20 @@ -import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core"; +import {Component, EventEmitter, forwardRef, Input, OnInit, Output} from "@angular/core"; import {ControlContainer, NgForm} from "@angular/forms"; import {Answers} from "@app/models/reciever/reciever-tip-data"; import {Field} from "@app/models/resolvers/field-template-model"; import {Step} from "@app/models/whistleblower/wb-tip-data"; import {SubmissionService} from "@app/services/helper/submission.service"; +import { FormComponent } from "../../form/form.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; + @Component({ - selector: "src-whistleblower-identity-field", - templateUrl: "./whistleblower-identity-field.component.html", - viewProviders: [{provide: ControlContainer, useExisting: NgForm}] + selector: "src-whistleblower-identity-field", + templateUrl: "./whistleblower-identity-field.component.html", + viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], + standalone: true, + imports: [forwardRef(() => FormComponent), TranslateModule, TranslatorPipe] }) export class WhistleblowerIdentityFieldComponent implements OnInit { @Input() submission: SubmissionService; diff --git a/client/app/src/pages/whistleblower/form-field-input/form-field-input.component.html b/client/app/src/pages/whistleblower/form-field-input/form-field-input.component.html index 079aa2d5de..b9e4410a4f 100644 --- a/client/app/src/pages/whistleblower/form-field-input/form-field-input.component.html +++ b/client/app/src/pages/whistleblower/form-field-input/form-field-input.component.html @@ -1,149 +1,247 @@
-
-
- +
+ @switch (field.template_id) { + @case ('whistleblower_identity') { +
+
-
-
+ } + @default { +
+ @switch (field.type) { + @case ('textarea') { +
-
-
{{entry['value'].length}}/{{field.attrs.max_len.value}} -
-
-
- {{'This field is mandatory'|translate}} - {{'The answer is too short'|translate}} -
-
-
- - - -
+ @for (option of field.options | orderBy:field.attrs.display_alphabetically.value ? 'label' : 'order'; track option; let index = $index) { + + } + + } + @if (field.options.length >= 10) { +
- {{option.label}} + @for (option of field.options | orderBy:field.attrs.display_alphabetically.value ? 'label' : 'order'; track option) { + {{option.label}} + } - + @if (entry['value']) { + - -
-
-
-
+ + } +
+ } +
+ } + @case ('multichoice') { +
+ @for (option of field.options | orderBy:'order'; track option; let innerindex = $index) { +
- -
-
-
-
+ +
+ } +
+ } + @case ('checkbox') { +
+ @for (option of field.options | orderBy:field.attrs.display_alphabetically.value ? 'label' : 'order'; track option; let index = $index) { +
- -
-
-
+ +
+ } +
+ } + @case ('date') { +
- - - - + + + +
-
-
+
+ } + @case ('daterange') { +
- + -
- - - - -
+
+ + + + +
- + -
- - - - -
+
+ + + + +
- + @if (input_start_date) { + - -
-
+ + } +
+ } + @case ('tos') { +
-
+ @if (field.attrs.attachment.value) { + +
+ }
- - + +
-
-
+
+ } + @case ('voice') { +
-
-
+
+ } + @case ('fileupload') { +
-
-
-
-
+
+ } + @case ('fieldgroup') { +
+
+ @switch (field.attrs.multimedia_type.value) { + @case ('image') { +
media -
-
+
+ } + @case ('audio') { +
-
-
+
+ } + @case ('video') { +
-
+
+ } + }
-
-
+
+ } + @default { +
-
{{'The specified input is not valid.'}}
-
- {{'The specified input is not valid:'|translate}} - - {{'please enter a valid email address.'|translate}} - {{'please enter numbers only.'|translate}} - {{'please enter numbers only.'}} - + @if (entry['value'] && defaultinput?.errors?.['pattern'] && entry['value'].length && entry['value'].length>0 && field.attrs.input_validation.value === 'custom') { +
{{'The specified input is not valid.'}}
+ } + @if (entry['value'] && defaultinput?.errors?.['pattern'] && entry['value'].length && entry['value'].length>0 && field.attrs.input_validation.value !== 'custom') { +
+ {{'The specified input is not valid:'|translate}} + + @switch (field.attrs.input_validation.value) { + @case ('email') { + {{'please enter a valid email address.'|translate}} + } + @case ('number') { + {{'please enter numbers only.'|translate}} + } + @case ('phonenumber') { + {{'please enter numbers only.'}} + } + } +
-
{{entry['value'].length}}/{{field.attrs.max_len.value}} + } + @if (entry['value'] && entry['value'].length && field.attrs.max_len.value > 0 && (field.attrs.max_len.value - entry['value']) <= 50) { +
{{entry['value'].length}}/{{field.attrs.max_len.value}}
-
- {{'This field is mandatory'|translate}} - {{'The answer is too short'|translate}} + } + @if (entry.required_status && displayErrors) { +
+ } + @if (defaultinput?.errors?.['required'] && displayErrors) { + {{'This field is mandatory'|translate}} + } + @if (defaultinput?.errors?.['minlength'] && displayErrors) { + {{'The answer is too short'|translate}} + }
-
+
+ } + }
-
-
{{'This field is mandatory'|translate}}
-
-
-
- {{option.hint1}} + } + } +
+ @if (field.type !== 'inputbox' && field.type !== 'textarea' && entry.required_status && (displayErrors || validateUploadSubmission())) { +
{{'This field is mandatory'|translate}}
+ } + @if (field.type === 'selectbox') { +
+ @for (option of field.options; track option) { +
+ @if (option.set && option.hint1) { +
+ {{option.hint1}}
+ }
+ }
-
-
- {{option.hint2}} + } + @for (option of field.options; track option) { +
+ @if (option.set && option.hint2) { +
+ {{option.hint2}}
+ }
+ }
diff --git a/client/app/src/pages/whistleblower/form-field-input/form-field-input.component.ts b/client/app/src/pages/whistleblower/form-field-input/form-field-input.component.ts index 788dbb6998..f76d96f1e3 100644 --- a/client/app/src/pages/whistleblower/form-field-input/form-field-input.component.ts +++ b/client/app/src/pages/whistleblower/form-field-input/form-field-input.component.ts @@ -1,18 +1,33 @@ -import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core"; +import { Component, EventEmitter, forwardRef, Input, OnInit, Output, inject } from "@angular/core"; import {FieldUtilitiesService} from "@app/shared/services/field-utilities.service"; -import {ControlContainer, NgForm} from "@angular/forms"; +import { ControlContainer, NgForm, FormsModule } from "@angular/forms"; import {SubmissionService} from "@app/services/helper/submission.service"; -import {NgbDateStruct} from "@ng-bootstrap/ng-bootstrap"; +import { NgbDateStruct, NgbInputDatepicker } from "@ng-bootstrap/ng-bootstrap"; import {Answers} from "@app/models/reciever/reciever-tip-data"; import {Step} from "@app/models/whistleblower/wb-tip-data"; import {Field} from "@app/models/resolvers/field-template-model"; +import { NgClass } from "@angular/common"; +import { WhistleblowerIdentityFieldComponent } from "../fields/whistleblower-identity-field/whistleblower-identity-field.component"; +import { NgSelectComponent, NgOptionComponent } from "@ng-select/ng-select"; +import { MarkdownComponent } from "ngx-markdown"; +import { VoiceRecorderComponent } from "../../../shared/partials/voice-recorder/voice-recorder.component"; +import { RFileUploadButtonComponent } from "../../../shared/partials/rfile-upload-button/r-file-upload-button.component"; +import { FormComponent } from "../form/form.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { StripHtmlPipe } from "@app/shared/pipes/strip-html.pipe"; +import { OrderByPipe } from "@app/shared/pipes/order-by.pipe"; @Component({ - selector: "src-form-field-input", - templateUrl: "./form-field-input.component.html", - viewProviders: [{provide: ControlContainer, useExisting: NgForm}] + selector: "src-form-field-input", + templateUrl: "./form-field-input.component.html", + viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], + standalone: true, + imports: [FormsModule, forwardRef(() => WhistleblowerIdentityFieldComponent), NgClass, NgSelectComponent, NgOptionComponent, NgbInputDatepicker, MarkdownComponent, VoiceRecorderComponent, RFileUploadButtonComponent, forwardRef(() => FormComponent), TranslateModule, TranslatorPipe, StripHtmlPipe, OrderByPipe] }) export class FormFieldInputComponent implements OnInit { + private fieldUtilitiesService = inject(FieldUtilitiesService); + @Input() field: any; @Input() index: number; @@ -42,9 +57,6 @@ export class FormFieldInputComponent implements OnInit { dateOptions2: NgbDateStruct; dateOptions: {min_date:NgbDateStruct,max_date:NgbDateStruct}={min_date:{year:0,month:0,day:0},max_date:{year:0,month:0,day:0}} - constructor(private fieldUtilitiesService: FieldUtilitiesService) { - } - clearDateRange() { this.input_start_date = ""; this.input_end_date = {year: 0, month: 0, day: 0}; diff --git a/client/app/src/pages/whistleblower/form-field-inputs/form-field-inputs.component.html b/client/app/src/pages/whistleblower/form-field-inputs/form-field-inputs.component.html index b844baeb31..019b5996dd 100644 --- a/client/app/src/pages/whistleblower/form-field-inputs/form-field-inputs.component.html +++ b/client/app/src/pages/whistleblower/form-field-inputs/form-field-inputs.component.html @@ -1,62 +1,94 @@ -
-
+} +@if ((['checkbox', 'fieldgroup', 'multichoice'].indexOf(field.type) !== -1) || (['whistleblower_identity'].indexOf(field.template_id) !== -1)) { +
+ @if (field.label) { + {{field.label|translate}} + } + @if (field.hint) { + + } + @if (field.required) { + * + }
- -
-
- - -
-
- -
-
+ @if (field.description) { + + } +
+ @for (entry of entries; track entry; let entryIndex = $index; let first = $first; let last = $last) { +
+ @if (entries.length > 1) { + + } + +
+ } + @if (field.multi_entry && field.type === 'fieldgroup') { +
+ +
+ } +
-
\ No newline at end of file + +} \ No newline at end of file diff --git a/client/app/src/pages/whistleblower/form-field-inputs/form-field-inputs.component.ts b/client/app/src/pages/whistleblower/form-field-inputs/form-field-inputs.component.ts index 982529c1be..c2303b9412 100644 --- a/client/app/src/pages/whistleblower/form-field-inputs/form-field-inputs.component.ts +++ b/client/app/src/pages/whistleblower/form-field-inputs/form-field-inputs.component.ts @@ -1,4 +1,4 @@ -import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core"; +import { Component, EventEmitter, forwardRef, Input, OnInit, Output, inject } from "@angular/core"; import {UtilsService} from "@app/shared/services/utils.service"; import {ControlContainer, NgForm} from "@angular/forms"; import {SubmissionService} from "@app/services/helper/submission.service"; @@ -6,13 +6,30 @@ import {Answers} from "@app/models/reciever/reciever-tip-data"; import {Step} from "@app/models/whistleblower/wb-tip-data"; import {Field} from "@app/models/resolvers/field-template-model"; import {cloneDeep} from "lodash-es"; +import { NgClass } from "@angular/common"; +import { MarkdownComponent } from "ngx-markdown"; +import { FormFieldInputComponent } from "../form-field-input/form-field-input.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { StripHtmlPipe } from "@app/shared/pipes/strip-html.pipe"; @Component({ - selector: "src-form-field-inputs", - templateUrl: "./form-field-inputs.component.html", - viewProviders: [{provide: ControlContainer, useExisting: NgForm}], + selector: "src-form-field-inputs", + templateUrl: "./form-field-inputs.component.html", + viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], + standalone: true, + imports: [ + MarkdownComponent, + NgClass, + forwardRef(() => FormFieldInputComponent), + TranslateModule, + TranslatorPipe, + StripHtmlPipe +], }) export class FormFieldInputsComponent implements OnInit { + protected utilsService = inject(UtilsService); + @Input() field: Field; @Input() fieldRow: number; @Input() fieldCol: number; @@ -33,9 +50,6 @@ export class FormFieldInputsComponent implements OnInit { fieldId: string; entries: { [key: string]: Field }[] = []; - constructor(protected utilsService: UtilsService) { - } - ngOnInit(): void { if(!this.fieldEntry){ this.fieldId = this.stepId + "-field-" + this.fieldRow + "-" + this.fieldCol; diff --git a/client/app/src/pages/whistleblower/form/form.component.html b/client/app/src/pages/whistleblower/form/form.component.html index edad06ba8b..077c196044 100644 --- a/client/app/src/pages/whistleblower/form/form.component.html +++ b/client/app/src/pages/whistleblower/form/form.component.html @@ -1,9 +1,11 @@ -
- - -
- -
-
-
-
\ No newline at end of file +@for (row of rows; track row; let fieldRow = $index) { +
+ @for (field of row | orderBy:'x' ;let fieldCol = $index; track field) { + @if (field.enabled) { +
+ +
+ } + } +
+} \ No newline at end of file diff --git a/client/app/src/pages/whistleblower/form/form.component.ts b/client/app/src/pages/whistleblower/form/form.component.ts index be92b5499a..38ff4b79d7 100644 --- a/client/app/src/pages/whistleblower/form/form.component.ts +++ b/client/app/src/pages/whistleblower/form/form.component.ts @@ -1,16 +1,27 @@ -import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core"; +import { Component, EventEmitter, forwardRef, Input, OnInit, Output, inject } from "@angular/core"; import {FieldUtilitiesService} from "@app/shared/services/field-utilities.service"; import {ControlContainer, NgForm} from "@angular/forms"; import {SubmissionService} from "@app/services/helper/submission.service"; import {Answers} from "@app/models/reciever/reciever-tip-data"; import {Children, Step} from "@app/models/whistleblower/wb-tip-data"; +import { NgClass } from "@angular/common"; +import { FormFieldInputsComponent } from "../form-field-inputs/form-field-inputs.component"; +import { OrderByPipe } from "@app/shared/pipes/order-by.pipe"; @Component({ - selector: "src-form", - templateUrl: "./form.component.html", - viewProviders: [{provide: ControlContainer, useExisting: NgForm}], + selector: "src-form", + templateUrl: "./form.component.html", + viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], + standalone: true, + imports: [ + NgClass, + forwardRef(() => FormFieldInputsComponent), + OrderByPipe +], }) export class FormComponent implements OnInit { + protected fieldUtilitiesService = inject(FieldUtilitiesService); + @Input() step: Step; @Input() index: number; @Input() answers: Answers; @@ -28,9 +39,6 @@ export class FormComponent implements OnInit { rows: any; status: { opened: boolean }; - constructor(protected fieldUtilitiesService: FieldUtilitiesService) { - } - ngOnInit(): void { this.initialize(); } diff --git a/client/app/src/pages/whistleblower/homepage/homepage.component.html b/client/app/src/pages/whistleblower/homepage/homepage.component.html index 3e2bc2d37d..9372939cae 100644 --- a/client/app/src/pages/whistleblower/homepage/homepage.component.html +++ b/client/app/src/pages/whistleblower/homepage/homepage.component.html @@ -1,22 +1,36 @@
- -
-
- -
-
-

{{'You are connecting to the server without anonymity and this server supports only anonymous submissions'|translate}}

-
-
- - -
-
-
- + @if (appDataService.public.node.presentation) { + + } +
+ @if (appDataService.public.node.disable_submissions || (!appDataService.public.node.https_whistleblower && !appDataService.connection.tor)) { +
+ +
+ } + @if (!appDataService.public.node.https_whistleblower && !appDataService.connection.tor) { +
+

{{'You are connecting to the server without anonymity and this server supports only anonymous submissions'|translate}}

+
+ } + @if (!appDataService.public.node.disable_submissions && (appDataService.public.node.https_whistleblower || appDataService.connection.tor)) { +
+ @if (appDataService.public.node.whistleblowing_question) { + + } + +
+ } +
+ @if (appDataService.public.node.https_whistleblower || appDataService.connection.tor) { +
+
+ }
-
+
diff --git a/client/app/src/pages/whistleblower/homepage/homepage.component.ts b/client/app/src/pages/whistleblower/homepage/homepage.component.ts index 37991298a5..48475ba18f 100644 --- a/client/app/src/pages/whistleblower/homepage/homepage.component.ts +++ b/client/app/src/pages/whistleblower/homepage/homepage.component.ts @@ -1,18 +1,27 @@ -import {Component} from "@angular/core"; +import { Component, inject } from "@angular/core"; import {AppDataService} from "@app/app-data.service"; import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; import {DisclaimerComponent} from "@app/shared/modals/disclaimer/disclaimer.component"; import {Observable} from "rxjs"; import {AppConfigService} from "@app/services/root/app-config.service"; +import { MarkdownComponent } from "ngx-markdown"; +import { ReceiptComponent } from "../../../shared/partials/receipt/receipt.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { StripHtmlPipe } from "@app/shared/pipes/strip-html.pipe"; + @Component({ - selector: "src-homepage", - templateUrl: "./homepage.component.html" + selector: "src-homepage", + templateUrl: "./homepage.component.html", + standalone: true, + imports: [MarkdownComponent, ReceiptComponent, TranslateModule, TranslatorPipe, StripHtmlPipe] }) export class HomepageComponent { + protected appConfigService = inject(AppConfigService); + protected appDataService = inject(AppDataService); + private modalService = inject(NgbModal); - constructor(protected appConfigService: AppConfigService, protected appDataService: AppDataService, private modalService: NgbModal) { - } openSubmission() { if (this.appDataService.public.node.disclaimer_text) { diff --git a/client/app/src/pages/whistleblower/receipt/receipt.component.html b/client/app/src/pages/whistleblower/receipt/receipt.component.html index 2483570505..139ae44308 100644 --- a/client/app/src/pages/whistleblower/receipt/receipt.component.html +++ b/client/app/src/pages/whistleblower/receipt/receipt.component.html @@ -1,32 +1,36 @@ -
+@if (appDataService.page === 'receiptpage') { +
- {{'Thank you.' | translate}} - {{'Your report was successful.' | translate}} - {{'We will try to get back to you as soon as possible.' | translate}} + {{'Thank you.' | translate}} + {{'Your report was successful.' | translate}} + {{'We will try to get back to you as soon as possible.' | translate}}
-
+
+}
-
-
- -
- -
-
- - - - -
-
-
-
{{'Use the 16 digit receipt to log in. It will allow you to view any messages we sent you, and also to add extra info.' | translate}}
-
- -
+
+
+ +
+ +
+
+ + + +
-
+
+ +
{{'Use the 16 digit receipt to log in. It will allow you to view any messages we sent you, and also to add extra info.' | translate}}
+ @if (appDataService.page === 'receiptpage') { +
+ +
+ } +
+
\ No newline at end of file diff --git a/client/app/src/pages/whistleblower/receipt/receipt.component.ts b/client/app/src/pages/whistleblower/receipt/receipt.component.ts index 7d725432cb..0579036818 100644 --- a/client/app/src/pages/whistleblower/receipt/receipt.component.ts +++ b/client/app/src/pages/whistleblower/receipt/receipt.component.ts @@ -1,20 +1,28 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {UtilsService} from "@app/shared/services/utils.service"; import {AuthenticationService} from "@app/services/helper/authentication.service"; import {AppDataService} from "@app/app-data.service"; import {AppConfigService} from "@app/services/root/app-config.service"; +import { FormsModule } from "@angular/forms"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; + @Component({ - selector: "src-receipt-whistleblower", - templateUrl: "./receipt.component.html" + selector: "src-receipt-whistleblower", + templateUrl: "./receipt.component.html", + standalone: true, + imports: [FormsModule, TranslateModule, TranslatorPipe] }) export class ReceiptComponent implements OnInit { + private appConfigService = inject(AppConfigService); + protected utilsService = inject(UtilsService); + protected authenticationService = inject(AuthenticationService); + protected appDataService = inject(AppDataService); + receipt: string; receiptId: string = ""; - constructor(private appConfigService: AppConfigService,protected utilsService: UtilsService, protected authenticationService: AuthenticationService, protected appDataService: AppDataService) { - } - public ngOnInit(): void { if (this.authenticationService.session.receipt) { this.receipt = this.authenticationService.session.receipt; diff --git a/client/app/src/pages/whistleblower/receiver-card/receiver-card.component.html b/client/app/src/pages/whistleblower/receiver-card/receiver-card.component.html index 4b93f64a3f..bd834b7bb1 100644 --- a/client/app/src/pages/whistleblower/receiver-card/receiver-card.component.html +++ b/client/app/src/pages/whistleblower/receiver-card/receiver-card.component.html @@ -1,16 +1,22 @@
-
-
- - -
-
-
-
- receiver picture -
-
-
-
{{receiverModel.description}}
+
+
+ @if (!receiverModel.forcefully_selected) { + + } +
+
+
+ @if (receiverModel.picture) { +
+ receiver picture +
+ } +
+
+ @if (receiverModel.description) { +
{{receiverModel.description}}
+ } +
\ No newline at end of file diff --git a/client/app/src/pages/whistleblower/receiver-card/receiver-card.component.ts b/client/app/src/pages/whistleblower/receiver-card/receiver-card.component.ts index ddca5998b8..c013fbc222 100644 --- a/client/app/src/pages/whistleblower/receiver-card/receiver-card.component.ts +++ b/client/app/src/pages/whistleblower/receiver-card/receiver-card.component.ts @@ -1,18 +1,22 @@ -import {Component, Input} from "@angular/core"; +import { Component, Input, inject } from "@angular/core"; import {Receiver} from "@app/models/app/public-model"; import {SubmissionService} from "@app/services/helper/submission.service"; import {TranslateService} from "@ngx-translate/core"; +import { NgClass, NgOptimizedImage } from "@angular/common"; +import { FormsModule } from "@angular/forms"; @Component({ - selector: "src-receiver-card", - templateUrl: "./receiver-card.component.html" + selector: "src-receiver-card", + templateUrl: "./receiver-card.component.html", + standalone: true, + imports: [NgClass, FormsModule, NgOptimizedImage] }) export class ReceiverCardComponent { + protected translate = inject(TranslateService); + @Input() submission: SubmissionService; @Input() receiverModel: Receiver; - constructor(protected translate: TranslateService) {} - selectable(): boolean { if (this.submission.context.maximum_selectable_receivers === 0) { return true; diff --git a/client/app/src/pages/whistleblower/receiver-selection/receiver-selection.component.html b/client/app/src/pages/whistleblower/receiver-selection/receiver-selection.component.html index e47742b5a1..b90dc251cb 100644 --- a/client/app/src/pages/whistleblower/receiver-selection/receiver-selection.component.html +++ b/client/app/src/pages/whistleblower/receiver-selection/receiver-selection.component.html @@ -1,25 +1,39 @@
-
{{'Recipient selection'|translate}}
-
-
{{'Select the recipients of your report'|translate}}
-
-
- {{'Recipients selected:'|translate}} {{submission.countSelectedReceivers()}} /{{submission.context.maximum_selectable_receivers}} -
-
{{'You have reached the maximum number of selectable recipients.'|translate}}
-
-
-
- -
+ @if (!show_steps_navigation_bar) { +
{{'Recipient selection'|translate}}
+ } + @if (submission.optional_receivers) { +
+
{{'Select the recipients of your report'|translate}}
+ @if (submission.context.maximum_selectable_receivers !== 0 && !submission.context.select_all_receivers) { +
+
+ {{'Recipients selected:'|translate}} {{submission.countSelectedReceivers()}} /{{submission.context.maximum_selectable_receivers}} +
+ @if (submission.countSelectedReceivers() === submission.context.maximum_selectable_receivers) { +
{{'You have reached the maximum number of selectable recipients.'|translate}}
+ }
+ } +
+ @for (receiver of submission.receivers | filter : 'forcefully_selected' : false | orderBy:receiversOrderPredicate; track receiver; let index = $index) { +
+ +
+ } +
-
-
{{'The following recipients will receive your report and could not be deselected:'|translate}}
-
-
- -
-
+ } + @if (submission.mandatory_receivers) { +
+
{{'The following recipients will receive your report and could not be deselected:'|translate}}
+
+ @for (receiver of submission.receivers | filter : 'forcefully_selected' : true | orderBy:receiversOrderPredicate; track receiver; let index = $index) { +
+ +
+ } +
+ }
diff --git a/client/app/src/pages/whistleblower/receiver-selection/receiver-selection.component.ts b/client/app/src/pages/whistleblower/receiver-selection/receiver-selection.component.ts index 835acbe7db..1312bf99ac 100644 --- a/client/app/src/pages/whistleblower/receiver-selection/receiver-selection.component.ts +++ b/client/app/src/pages/whistleblower/receiver-selection/receiver-selection.component.ts @@ -1,16 +1,24 @@ -import {Component, EventEmitter, Input, Output} from "@angular/core"; +import { Component, EventEmitter, Input, Output, inject } from "@angular/core"; import {SubmissionService} from "@app/services/helper/submission.service"; import {UtilsService} from "@app/shared/services/utils.service"; +import { NgClass } from "@angular/common"; +import { ReceiverCardComponent } from "../receiver-card/receiver-card.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { FilterPipe } from "@app/shared/pipes/filter.pipe"; +import { OrderByPipe } from "@app/shared/pipes/order-by.pipe"; @Component({ - selector: "src-receiver-selection", - templateUrl: "./receiver-selection.component.html" + selector: "src-receiver-selection", + templateUrl: "./receiver-selection.component.html", + standalone: true, + imports: [NgClass, ReceiverCardComponent, TranslateModule, TranslatorPipe, FilterPipe, OrderByPipe] }) export class ReceiverSelectionComponent { + protected utilsService = inject(UtilsService); + @Input() show_steps_navigation_bar: boolean; @Input() submission: SubmissionService; @Input() receiversOrderPredicate: string; @Output() switchSelection: EventEmitter = new EventEmitter(); - - constructor(protected utilsService: UtilsService) {} } diff --git a/client/app/src/pages/whistleblower/step-error/step-error.component.html b/client/app/src/pages/whistleblower/step-error/step-error.component.html index e48ad248c9..9cfa43c6cf 100644 --- a/client/app/src/pages/whistleblower/step-error/step-error.component.html +++ b/client/app/src/pages/whistleblower/step-error/step-error.component.html @@ -1,8 +1,10 @@ -
+@if (stepForm.invalid) { +

{{'In this step the answers to the following questions are either missing or invalid:'|translate}}

    - - - + @for (error of getInnerGroupErrors(stepForm); track error) { + + }
-
+
+} diff --git a/client/app/src/pages/whistleblower/step-error/step-error.component.ts b/client/app/src/pages/whistleblower/step-error/step-error.component.ts index 4ca75b7694..f393d53060 100644 --- a/client/app/src/pages/whistleblower/step-error/step-error.component.ts +++ b/client/app/src/pages/whistleblower/step-error/step-error.component.ts @@ -3,9 +3,15 @@ import {FormArray, FormGroup, NgForm} from "@angular/forms"; import {Field} from "@app/models/resolvers/field-template-model"; import {DisplayStepErrorsFunction, StepFormFunction} from "@app/shared/constants/types"; +import { StepErrorEntryComponent } from "./template/step-error-entry/step-error-entry.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; + @Component({ - selector: "src-step-error", - templateUrl: "./step-error.component.html" + selector: "src-step-error", + templateUrl: "./step-error.component.html", + standalone: true, + imports: [StepErrorEntryComponent, TranslateModule, TranslatorPipe] }) export class StepErrorComponent { @Input() navigation: number; diff --git a/client/app/src/pages/whistleblower/step-error/template/step-error-entry/step-error-entry.component.html b/client/app/src/pages/whistleblower/step-error/template/step-error-entry/step-error-entry.component.html index 64aa2a0233..7a140ee72e 100644 --- a/client/app/src/pages/whistleblower/step-error/template/step-error-entry/step-error-entry.component.html +++ b/client/app/src/pages/whistleblower/step-error/template/step-error-entry/step-error-entry.component.html @@ -1 +1,3 @@ -
  • {{field.label}}
  • +@if (field) { +
  • {{field.label}}
  • +} diff --git a/client/app/src/pages/whistleblower/step-error/template/step-error-entry/step-error-entry.component.ts b/client/app/src/pages/whistleblower/step-error/template/step-error-entry/step-error-entry.component.ts index 1574414915..a3b8ba001c 100644 --- a/client/app/src/pages/whistleblower/step-error/template/step-error-entry/step-error-entry.component.ts +++ b/client/app/src/pages/whistleblower/step-error/template/step-error-entry/step-error-entry.component.ts @@ -2,9 +2,12 @@ import {Component, Input, OnInit} from "@angular/core"; import {NgForm} from "@angular/forms"; import {Field} from "@app/models/resolvers/field-template-model"; + @Component({ - selector: "src-step-error-entry", - templateUrl: "./step-error-entry.component.html" + selector: "src-step-error-entry", + templateUrl: "./step-error-entry.component.html", + standalone: true, + imports: [] }) export class StepErrorEntryComponent implements OnInit { @Input() err: string; diff --git a/client/app/src/pages/whistleblower/submission/submission.component.html b/client/app/src/pages/whistleblower/submission/submission.component.html index aad51111d8..ba2261245c 100644 --- a/client/app/src/pages/whistleblower/submission/submission.component.html +++ b/client/app/src/pages/whistleblower/submission/submission.component.html @@ -1,68 +1,106 @@ - -
    - +@if (submissionService) { + @if (selectable_contexts.length && !submissionService.context) { +
    +
    -
    -
    -
    {{submissionService.context.name}}
    -
    -
    - context picture -
    -
    + } + @if (submissionService.context) { +
    + @if (selectable_contexts.length > 1 || context_id) { +
    +
    {{submissionService.context.name}}
    +
    + @if (submissionService.context.picture) { +
    + context picture +
    + } +
    -
    -
    - -
    -
    - -
    -
    - -
    -
    -
    -
    -
    -
    - -
    - - -
    + } + +
    + @if (submissionService.context.show_steps_navigation_interface && submissionService.context.questionnaire.steps.length > 1) { + + } +
    + @if (validate[navigation] && !hasPreviousStepValue && !areReceiversSelectedValue) { +
    + +
    + } + @if (submissionService.context.allow_recipients_selection && navigation === -1) { +
    + +
    + } + @if (navigation !== -1) { +
    + @for (step of submissionService.context.questionnaire.steps | orderBy:'order'; track step; let index = $index) { +
    + @if (step && step.enabled && submissionService) { +
    +
    + @if (validate[navigation] && stepForm.invalid) { +
    +
    + } + @if (step.description) { + + } +
    -
    -
    -
    -
    - - - - -
    - +
    + } +
    + } +
    + } +
    +
    +
    + @if (hasPreviousStep()) { + + } + @if (hasNextStep()) { + + } + @if (!hasNextStep() && uploading()) { + + } + @if (!hasNextStep()) { + + } +
    +
    - + } +} diff --git a/client/app/src/pages/whistleblower/submission/submission.component.ts b/client/app/src/pages/whistleblower/submission/submission.component.ts index dfaf865240..31353c3939 100644 --- a/client/app/src/pages/whistleblower/submission/submission.component.ts +++ b/client/app/src/pages/whistleblower/submission/submission.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit, SimpleChanges, QueryList, ViewChild, ViewChildren} from "@angular/core"; +import { Component, OnInit, SimpleChanges, QueryList, ViewChild, ViewChildren, inject } from "@angular/core"; import { ActivatedRoute } from '@angular/router'; import {AppDataService} from "@app/app-data.service"; import {WhistleblowerLoginResolver} from "@app/shared/resolvers/whistleblower-login.resolver"; @@ -6,7 +6,7 @@ import {FieldUtilitiesService} from "@app/shared/services/field-utilities.servic import {SubmissionService} from "@app/services/helper/submission.service"; import {UtilsService} from "@app/shared/services/utils.service"; import {AuthenticationService} from "@app/services/helper/authentication.service"; -import {NgForm} from "@angular/forms"; +import { NgForm, FormsModule } from "@angular/forms"; import {AppConfigService} from "@app/services/root/app-config.service"; import {Context, Questionnaire, Receiver} from "@app/models/app/public-model"; import {Answers} from "@app/models/reciever/reciever-tip-data"; @@ -15,13 +15,39 @@ import * as Flow from "@flowjs/flow.js"; import {TitleService} from "@app/shared/services/title.service"; import {Router} from "@angular/router"; import {WhistleblowerSubmissionService} from "@app/pages/whistleblower/whistleblower-submission.service"; +import { NgClass } from "@angular/common"; +import { ContextSelectionComponent } from "../context-selection/context-selection.component"; +import { ReceiverSelectionComponent } from "../receiver-selection/receiver-selection.component"; +import { NgFormChangeDirective } from "../../../shared/directive/ng-form-change.directive"; +import { StepErrorComponent } from "../step-error/step-error.component"; +import { MarkdownComponent } from "ngx-markdown"; +import { FormComponent } from "../form/form.component"; +import { RFilesUploadStatusComponent } from "../../../shared/partials/rfiles-upload-status/r-files-upload-status.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; +import { StripHtmlPipe } from "@app/shared/pipes/strip-html.pipe"; +import { OrderByPipe } from "@app/shared/pipes/order-by.pipe"; @Component({ - selector: "src-submission", - templateUrl: "./submission.component.html", - providers: [SubmissionService] + selector: "src-submission", + templateUrl: "./submission.component.html", + providers: [SubmissionService], + standalone: true, + imports: [ContextSelectionComponent, FormsModule, NgClass, ReceiverSelectionComponent, NgFormChangeDirective, StepErrorComponent, MarkdownComponent, FormComponent, RFilesUploadStatusComponent, TranslateModule, TranslatorPipe, StripHtmlPipe, OrderByPipe] }) export class SubmissionComponent implements OnInit { + private route = inject(ActivatedRoute); + protected whistleblowerSubmissionService = inject(WhistleblowerSubmissionService); + private titleService = inject(TitleService); + private router = inject(Router); + private appConfigService = inject(AppConfigService); + private whistleblowerLoginResolver = inject(WhistleblowerLoginResolver); + protected authenticationService = inject(AuthenticationService); + private appDataService = inject(AppDataService); + private utilsService = inject(UtilsService); + private fieldUtilitiesService = inject(FieldUtilitiesService); + submissionService = inject(SubmissionService); + @ViewChild("submissionForm") public submissionForm: NgForm; @ViewChildren("stepForm") stepForms: QueryList; @@ -45,7 +71,7 @@ export class SubmissionComponent implements OnInit { hasPreviousStepValue: boolean; areReceiversSelectedValue: boolean; - constructor(private route:ActivatedRoute, protected whistleblowerSubmissionService:WhistleblowerSubmissionService,private titleService: TitleService, private router: Router, private appConfigService: AppConfigService, private whistleblowerLoginResolver: WhistleblowerLoginResolver, protected authenticationService: AuthenticationService, private appDataService: AppDataService, private utilsService: UtilsService, private fieldUtilitiesService: FieldUtilitiesService, public submissionService: SubmissionService) { + constructor() { this.selectable_contexts = []; this.receivedData = this.submissionService.getSharedData(); diff --git a/client/app/src/pages/whistleblower/tippage/tippage.component.html b/client/app/src/pages/whistleblower/tippage/tippage.component.html index 96a3863302..ee6adc3f1c 100644 --- a/client/app/src/pages/whistleblower/tippage/tippage.component.html +++ b/client/app/src/pages/whistleblower/tippage/tippage.component.html @@ -1,43 +1,51 @@
    -
    - - - -
    +
    + + + +
    -
    +@if (shouldShowAdditionalQuestionnaire()) { +
    - +
    -
    +
    +}
    -
    - -
    +
    + +
    -
    +@if (wbTipService.tip.receivers && wbTipService.tip.receivers.length > 1) { +
    - +
    -
    +
    +}
    -
    - -
    -
    - -
    +
    + +
    + @if (wbTipService.tip.enable_whistleblower_identity) { +
    + +
    + }
    -
    - - -
    +
    + + @if (wbTipService.tip.wbfiles && wbTipService.tip.wbfiles.length) { + + } +
    -
    - - - -
    +
    + @if (wbTipService) { + + } +
    \ No newline at end of file diff --git a/client/app/src/pages/whistleblower/tippage/tippage.component.ts b/client/app/src/pages/whistleblower/tippage/tippage.component.ts index 3061cc1210..953d3621d8 100644 --- a/client/app/src/pages/whistleblower/tippage/tippage.component.ts +++ b/client/app/src/pages/whistleblower/tippage/tippage.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {WbTipResolver} from "@app/shared/resolvers/wb-tip-resolver.service"; import {FieldUtilitiesService} from "@app/shared/services/field-utilities.service"; import {ActivatedRoute} from "@angular/router"; @@ -9,12 +9,34 @@ import {UtilsService} from "@app/shared/services/utils.service"; import {Children, WbTipData} from "@app/models/whistleblower/wb-tip-data"; import {Answers, Questionnaire} from "@app/models/reciever/reciever-tip-data"; import {WhistleblowerIdentity} from "@app/models/app/shared-public-model"; +import { NgClass } from "@angular/common"; +import { TipAdditionalQuestionnaireInviteComponent } from "../../../shared/partials/tip-additional-questionnaire-invite/tip-additional-questionnaire-invite.component"; +import { TipInfoComponent } from "../../../shared/partials/tip-info/tip-info.component"; +import { TipReceiverListComponent } from "../../../shared/partials/tip-receiver-list/tip-receiver-list.component"; +import { TipQuestionnaireAnswersComponent } from "../../../shared/partials/tip-questionnaire-answers/tip-questionnaire-answers.component"; +import { WhistleblowerIdentityComponent } from "../../../shared/partials/whistleblower-identity/whistleblower-identity.component"; +import { TipFilesWhistleblowerComponent } from "../../../shared/partials/tip-files-whistleblower/tip-files-whistleblower.component"; +import { WidgetWbFilesComponent } from "../../../shared/partials/widget-wbfiles/widget-wb-files.component"; +import { TipCommentsComponent } from "../../../shared/partials/tip-comments/tip-comments.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-tippage", - templateUrl: "./tippage.component.html" + selector: "src-tippage", + templateUrl: "./tippage.component.html", + standalone: true, + imports: [TipAdditionalQuestionnaireInviteComponent, TipInfoComponent, TipReceiverListComponent, NgClass, TipQuestionnaireAnswersComponent, WhistleblowerIdentityComponent, TipFilesWhistleblowerComponent, WidgetWbFilesComponent, TipCommentsComponent, TranslateModule, TranslatorPipe] }) export class TippageComponent implements OnInit { + private fieldUtilities = inject(FieldUtilitiesService); + private wbTipResolver = inject(WbTipResolver); + private fieldUtilitiesService = inject(FieldUtilitiesService); + protected utilsService = inject(UtilsService); + protected appDataService = inject(AppDataService); + private activatedRoute = inject(ActivatedRoute); + private httpService = inject(HttpService); + protected wbTipService = inject(WbtipService); + fileUploadUrl: string; answers = {}; uploads: { [key: string]: any } = {}; @@ -28,9 +50,6 @@ export class TippageComponent implements OnInit { private submission: { _submission: WbTipData[] } = {_submission: []}; protected tip: WbTipData; - constructor(private fieldUtilities: FieldUtilitiesService, private wbTipResolver: WbTipResolver, private fieldUtilitiesService: FieldUtilitiesService, protected utilsService: UtilsService, protected appDataService: AppDataService, private activatedRoute: ActivatedRoute, private httpService: HttpService, protected wbTipService: WbtipService) { - } - ngOnInit() { const wpTip = this.wbTipResolver.dataModel; if (wpTip) { @@ -182,4 +201,11 @@ export class TippageComponent implements OnInit { onFormChange() { this.fieldUtilitiesService.onAnswersUpdate(this); } + + shouldShowAdditionalQuestionnaire(): boolean { + const tip = this.wbTipService.tip; + return tip?.status !== 'closed' && + !!tip?.context?.additional_questionnaire_id && + tip?.questionnaires?.length === 1; + } } diff --git a/client/app/src/pages/whistleblower/whistleblower.module.ts b/client/app/src/pages/whistleblower/whistleblower.module.ts deleted file mode 100644 index ede75cd367..0000000000 --- a/client/app/src/pages/whistleblower/whistleblower.module.ts +++ /dev/null @@ -1,72 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule, NgOptimizedImage} from "@angular/common"; -import {HomepageComponent} from "@app/pages/whistleblower/homepage/homepage.component"; -import {TranslateModule} from "@ngx-translate/core"; -import {SharedModule} from "@app/shared.module"; -import {MarkdownModule} from "ngx-markdown"; -import {TippageComponent} from "@app/pages/whistleblower/tippage/tippage.component"; -import { - WhistleblowerIdentityComponent -} from "@app/shared/partials/whistleblower-identity/whistleblower-identity.component"; -import {FormsModule, ReactiveFormsModule} from "@angular/forms"; -import {SubmissionComponent} from "@app/pages/whistleblower/submission/submission.component"; -import {ContextSelectionComponent} from "@app/pages/whistleblower/context-selection/context-selection.component"; -import {StepErrorComponent} from "@app/pages/whistleblower/step-error/step-error.component"; -import { - StepErrorEntryComponent -} from "@app/pages/whistleblower/step-error/template/step-error-entry/step-error-entry.component"; -import {ReceiverSelectionComponent} from "@app/pages/whistleblower/receiver-selection/receiver-selection.component"; -import {ReceiverCardComponent} from "@app/pages/whistleblower/receiver-card/receiver-card.component"; -import {FormComponent} from "@app/pages/whistleblower/form/form.component"; -import {FormFieldInputsComponent} from "@app/pages/whistleblower/form-field-inputs/form-field-inputs.component"; -import {FormFieldInputComponent} from "@app/pages/whistleblower/form-field-input/form-field-input.component"; -import { - WhistleblowerIdentityFieldComponent -} from "./fields/whistleblower-identity-field/whistleblower-identity-field.component"; -import {NgSelectModule} from "@ng-select/ng-select"; -import {NgbInputDatepicker} from "@ng-bootstrap/ng-bootstrap"; -import {ReceiptComponent} from "@app/pages/whistleblower/receipt/receipt.component"; -import { - TipAdditionalQuestionnaireFormComponent -} from "@app/shared/modals/tip-additional-questionnaire-form/tip-additional-questionnaire-form.component"; - - -@NgModule({ - declarations: [ - HomepageComponent, - TippageComponent, - WhistleblowerIdentityComponent, - SubmissionComponent, - ContextSelectionComponent, - StepErrorComponent, - StepErrorEntryComponent, - ReceiverSelectionComponent, - ReceiverCardComponent, - FormComponent, - FormFieldInputsComponent, - FormFieldInputComponent, - WhistleblowerIdentityFieldComponent, - ReceiptComponent, - TipAdditionalQuestionnaireFormComponent, - ], - exports: [ - HomepageComponent, - TippageComponent, - SubmissionComponent, - WhistleblowerIdentityFieldComponent, - ReceiptComponent - ], - imports: [ - CommonModule, - TranslateModule, - SharedModule, - MarkdownModule, - ReactiveFormsModule, - FormsModule, - NgSelectModule, - NgbInputDatepicker, - NgOptimizedImage, - ] -}) -export class WhistleblowerModule { -} diff --git a/client/app/src/pages/wizard/wizard-routing.module.ts b/client/app/src/pages/wizard/wizard-routing.module.ts deleted file mode 100644 index 10a62cd73a..0000000000 --- a/client/app/src/pages/wizard/wizard-routing.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {NgModule} from "@angular/core"; -import {RouterModule, Routes} from "@angular/router"; -import {WizardComponent} from "@app/pages/wizard/wizard/wizard.component"; - -const routes: Routes = [ - { - path: "", - component: WizardComponent, - pathMatch: "full", - } - -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], -}) -export class WizardRoutingModule { -} diff --git a/client/app/src/pages/wizard/wizard.module.ts b/client/app/src/pages/wizard/wizard.module.ts deleted file mode 100644 index 362a0acccd..0000000000 --- a/client/app/src/pages/wizard/wizard.module.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {TranslateModule} from "@ngx-translate/core"; -import {FormsModule, ReactiveFormsModule} from "@angular/forms"; -import {NgSelectModule} from "@ng-select/ng-select"; -import {SharedModule} from "@app/shared.module"; -import {WizardComponent} from "@app/pages/wizard/wizard/wizard.component"; -import {ProfileComponent} from "@app/pages/wizard/wizard/template/profile/profile.component"; - - -@NgModule({ - declarations: [ - WizardComponent, - ProfileComponent - ], - imports: [ - CommonModule, - TranslateModule, - FormsModule, - ReactiveFormsModule, - NgSelectModule, - SharedModule - ] -}) -export class WizardModule { -} diff --git a/client/app/src/pages/wizard/wizard.routes.ts b/client/app/src/pages/wizard/wizard.routes.ts new file mode 100644 index 0000000000..9d5f3d6167 --- /dev/null +++ b/client/app/src/pages/wizard/wizard.routes.ts @@ -0,0 +1,9 @@ +import {Routes} from "@angular/router"; + +export const wizardRoutes: Routes = [ + { + path: "", + loadComponent: () => import('@app/pages/wizard/wizard/wizard.component').then(m => m.WizardComponent), + pathMatch: "full", + } +]; \ No newline at end of file diff --git a/client/app/src/pages/wizard/wizard/template/profile/profile.component.ts b/client/app/src/pages/wizard/wizard/template/profile/profile.component.ts index 6bb044250e..96fd47bc1d 100644 --- a/client/app/src/pages/wizard/wizard/template/profile/profile.component.ts +++ b/client/app/src/pages/wizard/wizard/template/profile/profile.component.ts @@ -1,8 +1,9 @@ import {Component} from "@angular/core"; @Component({ - selector: "src-profile", - templateUrl: "./profile.component.html" + selector: "src-profile", + templateUrl: "./profile.component.html", + standalone: true }) export class ProfileComponent { diff --git a/client/app/src/pages/wizard/wizard/wizard.component.html b/client/app/src/pages/wizard/wizard/wizard.component.html index 246d3b1784..073cb31fc8 100644 --- a/client/app/src/pages/wizard/wizard/wizard.component.html +++ b/client/app/src/pages/wizard/wizard/wizard.component.html @@ -1,270 +1,306 @@
    -
    -
    -
    -
    {{'Welcome!'|translate}}
    -
    {{'The following step-by-step procedure will guide you through creating your whistleblowing platform.'|translate}}
    -
    + @if (step === 1) { +
    +
    +
    +
    {{'Welcome!'|translate}}
    +
    {{'The following step-by-step procedure will guide you through creating your whistleblowing platform.'|translate}}
    -
    +
    +
    - +
    -
    -
    +
    + } + @if (step === 2) { +
    -
    -
    - -
    - -
    -
    +
    +
    + +
    + +
    +

    -
    +
    +
    -
    -
    -
    + } + @if (step === 3) { +
    +
    {{'Please choose a configuration profile:'|translate}}
    - +
    -
    -
    -
    -
    +
    +
    + - + -
    -
    -
    -
    -
    {{'Admin'|translate}}
    -
    + +
    +
    + } + @if (step === 4) { +
    +
    +
    {{'Admin'|translate}}
    +
    - -
    - -
    + +
    + +
    - -
    - -
    + +
    + +
    - -
    - -
    - {{'Invalid email address'|translate}} -
    -
    + +
    + + @if (admin_email?.errors?.['pattern']) { +
    + {{'Invalid email address'|translate}} +
    + } +
    - -
    - - -
    -

    {{'The chosen password is too weak. A valid password should be at least 12 characters long and contain a variety of characters including at least a lowercase character, a capital character, a number and a special character.'|translate}}

    -
    -
    + +
    + + @if (password_score>0) { + + } + @if (!password_admin?.errors?.['required'] && password_admin.errors?.['passwordStrengthValidator']) { +
    +

    {{'The chosen password is too weak. A valid password should be at least 12 characters long and contain a variety of characters including at least a lowercase character, a capital character, a number and a special character.'|translate}}

    +
    + } +
    - -
    - -
    {{'The two passwords do not match'|translate}}
    -
    + +
    + + @if (wizardFormStep4.valid && (!password_admin.errors?.['passwordStrengthValidator'] && wizard.admin_password && (wizard.admin_password !== admin_check_password))) { +
    {{'The two passwords do not match'|translate}}
    + } +
    -
    -
    - - -
    -
    {{'We advise selecting this option if you would like to protect data from being lost in the situation where recipients lose their passwords. On the other hand, we would not advise using this feature if you want to setup a system where only recipients are able to access submissions.'|translate}}
    +
    +
    + +
    +
    {{'We advise selecting this option if you would like to protect data from being lost in the situation where recipients lose their passwords. On the other hand, we would not advise using this feature if you want to setup a system where only recipients are able to access submissions.'|translate}}
    +
    +
    -
    -
    -
    - - -
    -
    -
    -
    -
    {{'Recipient'|translate}}
    -
    -
    -
    +
    +
    + + +
    +
    + } + @if (step === 5) { +
    +
    +
    {{'Recipient'|translate}}
    +
    + @if (!wizard.skip_recipient_account_creation) { +
    +
    - -
    - {{'Please choose a different username.'|translate}} + + @if (recipientDuplicate && wizard.receiver_username === wizard.admin_username) { +
    + {{'Please choose a different username.'|translate}}
    + }
    -
    -
    +
    +
    - +
    -
    -
    +
    +
    - -
    - {{'Invalid email address'|translate}} + + @if (receiver_email?.errors?.['pattern']) { +
    + {{'Invalid email address'|translate}}
    + }
    -
    -
    +
    +
    - - -
    -

    {{'The chosen password is too weak. A valid password should be at least 12 characters long and contain a variety of characters including at least a lowercase character, a capital character, a number and a special character.'|translate}}

    + + @if (password_score>0) { + + } + @if (!password_receiver?.errors?.['required'] && password_receiver?.errors?.['passwordStrengthValidator']) { +
    +

    {{'The chosen password is too weak. A valid password should be at least 12 characters long and contain a variety of characters including at least a lowercase character, a capital character, a number and a special character.'|translate}}

    + }
    -
    -
    +
    +
    - -
    {{'The two passwords do not match'|translate}}
    + + @if (validation.step4 && (!password_receiver?.errors?.['passwordStrengthValidator'] && recipient_check_password.length>0 && wizard.receiver_password && (wizard.receiver_password !== recipient_check_password))) { +
    {{'The two passwords do not match'|translate}}
    + }
    +
    -
    -
    + } +
    -
    - - -
    +
    + + +
    +
    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    -
    -
    {{'License'|translate}}
    -
    {{license|translate}}
    -
    +
    +
    +
    + + +
    +
    + } + @if (step === 6) { +
    +
    +
    +
    +
    {{'License'|translate}}
    +
    {{license|translate}}
    +
    +
    + @if (appDataService.public.node.root_tenant) { +
    + + +
    {{'By enabling this feature, you will contribute to the development and security of the platform.'|translate}}
    +
    + }
    -
    - - -
    {{'By enabling this feature, you will contribute to the development and security of the platform.'|translate}}
    -
    +
    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    -
    - -
    {{'You have completed the platform wizard.'|translate}}
    +
    +
    + + +
    -
    -
    -
    -
    - -
    -
    - + } + @if (step === 7) { +
    +
    +
    +
    + +
    {{'You have completed the platform wizard.'|translate}}
    +
    +
    +
    +
    +
    + +
    +
    + } + diff --git a/client/app/src/pages/wizard/wizard/wizard.component.ts b/client/app/src/pages/wizard/wizard/wizard.component.ts index eae200b8b9..37134396fc 100644 --- a/client/app/src/pages/wizard/wizard/wizard.component.ts +++ b/client/app/src/pages/wizard/wizard/wizard.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from "@angular/core"; +import { Component, OnInit, inject } from "@angular/core"; import {Constants} from "@app/shared/constants/constants"; import {Router} from "@angular/router"; import {HttpClient} from "@angular/common/http"; @@ -9,12 +9,31 @@ import {TranslationService} from "@app/services/helper/translation.service"; import {AppConfigService} from "@app/services/root/app-config.service"; import {TitleService} from "@app/shared/services/title.service"; import {UtilsService} from "@app/shared/services/utils.service"; +import { FormsModule } from "@angular/forms"; +import { NgClass } from "@angular/common"; +import { ProfileComponent } from "./template/profile/profile.component"; +import { PasswordStrengthValidatorDirective } from "../../../shared/directive/password-strength-validator.directive"; +import { PasswordMeterComponent } from "../../../shared/components/password-meter/password-meter.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { TranslatorPipe } from "@app/shared/pipes/translate"; @Component({ - selector: "src-wizard", - templateUrl: "./wizard.component.html" + selector: "src-wizard", + templateUrl: "./wizard.component.html", + standalone: true, + imports: [FormsModule, NgClass, ProfileComponent, PasswordStrengthValidatorDirective, PasswordMeterComponent, TranslateModule, TranslatorPipe] }) export class WizardComponent implements OnInit { + private titleService = inject(TitleService); + private translationService = inject(TranslationService); + private router = inject(Router); + private http = inject(HttpClient); + private authenticationService = inject(AuthenticationService); + private httpService = inject(HttpService); + protected appDataService = inject(AppDataService); + protected appConfigService = inject(AppConfigService); + private utilsService = inject(UtilsService); + step: number = 1; emailRegexp = Constants.emailRegexp; password_score = 0; @@ -49,9 +68,6 @@ export class WizardComponent implements OnInit { "enable_developers_exception_notification": false }; - constructor(private titleService: TitleService, private translationService: TranslationService, private router: Router, private http: HttpClient, private authenticationService: AuthenticationService, private httpService: HttpService, protected appDataService: AppDataService, protected appConfigService: AppConfigService, private utilsService: UtilsService) { - } - ngOnInit() { if (this.appDataService.public.node.wizard_done) { this.router.navigate(["/"]).then(_ => { diff --git a/client/app/src/services/helper/authentication.service.ts b/client/app/src/services/helper/authentication.service.ts index b6faee93da..430dda448f 100644 --- a/client/app/src/services/helper/authentication.service.ts +++ b/client/app/src/services/helper/authentication.service.ts @@ -1,4 +1,4 @@ -import {Injectable, SecurityContext} from "@angular/core"; +import { Injectable, inject } from "@angular/core"; import {LoginDataRef} from "@app/pages/auth/login/model/login-model"; import {HttpService} from "@app/shared/services/http.service"; import {Observable} from "rxjs"; @@ -10,20 +10,26 @@ import {TitleService} from "@app/shared/services/title.service"; import {HttpClient, HttpErrorResponse, HttpHeaders} from "@angular/common/http"; import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; import {OtkcAccessComponent} from "@app/shared/modals/otkc-access/otkc-access.component"; -import {DomSanitizer} from '@angular/platform-browser'; - @Injectable({ providedIn: "root" }) export class AuthenticationService { + private http = inject(HttpClient); + private modalService = inject(NgbModal); + private titleService = inject(TitleService); + private activatedRoute = inject(ActivatedRoute); + private httpService = inject(HttpService); + private appDataService = inject(AppDataService); + private router = inject(Router); + public session: any = undefined; permissions: { can_upload_files: boolean } loginInProgress: boolean = false; requireAuthCode: boolean = false; loginData: LoginDataRef = new LoginDataRef(); - constructor(private http: HttpClient, private modalService: NgbModal,private titleService: TitleService, private activatedRoute: ActivatedRoute, private httpService: HttpService, private appDataService: AppDataService, private router: Router, private sanitizer: DomSanitizer) { + constructor() { this.init(); } @@ -103,11 +109,9 @@ export class AuthenticationService { requestObservable.subscribe( { next: (response: Session) => { + this.reset() if (response.redirect) { - response.redirect = this.sanitizer.sanitize(SecurityContext.URL, response.redirect) || ''; - if (response.redirect) { - this.router.navigate([response.redirect]).then(); - } + this.router.navigate([response.redirect]).then(); } this.setSession(response); if (response && response && response.properties && response.properties.new_receipt) { @@ -130,39 +134,36 @@ export class AuthenticationService { }; return; } - - if (this.session.role === "whistleblower") { - if (password) { - this.appDataService.receipt = password; - this.titleService.setPage("tippage"); - } else if (this.session.properties.operator_session) { - this.router.navigate(['/']); - } + const src = this.activatedRoute.snapshot.queryParams['src']; + if (src) { + this.router.navigate([src]).then(); + location.replace(src); } else { - if (!callback) { - this.reset(); - - let redirect = this.activatedRoute.snapshot.queryParams['redirect'] || undefined; - redirect = this.activatedRoute.snapshot.queryParams['redirect'] || '/'; - redirect = decodeURIComponent(redirect); - - if (redirect !== "/") { - redirect = this.sanitizer.sanitize(SecurityContext.URL, redirect) || ''; - - // Honor only local redirects - if (redirect.startsWith("/")) { - this.router.navigate([redirect]); - } - } else { - this.appDataService.updateShowLoadingPanel(true); - this.router.navigate([this.session.homepage], { - queryParams: this.activatedRoute.snapshot.queryParams, - queryParamsHandling: "merge" - }).then(); + if (this.session.role === "whistleblower") { + if (password) { + this.appDataService.receipt = password; + this.titleService.setPage("tippage"); + } else if (this.session.properties.operator_session) { + this.router.navigate(['/']); + } + } else { + if (!callback) { + let redirect = this.activatedRoute.snapshot.queryParams['redirect'] || undefined; + this.reset(); + redirect = this.activatedRoute.snapshot.queryParams['redirect'] || '/'; + const redirectURL = decodeURIComponent(redirect); + if (redirectURL !== "/") { + this.router.navigate([redirectURL]); + } else { + this.appDataService.updateShowLoadingPanel(true); + this.router.navigate([this.session.homepage], { + queryParams: this.activatedRoute.snapshot.queryParams, + queryParamsHandling: "merge" + }).then(); + } } } } - if (callback) { callback(); } diff --git a/client/app/src/services/helper/custom-file-loader-service.service.ts b/client/app/src/services/helper/custom-file-loader-service.service.ts index bea361baf4..4bf1f23d59 100644 --- a/client/app/src/services/helper/custom-file-loader-service.service.ts +++ b/client/app/src/services/helper/custom-file-loader-service.service.ts @@ -1,12 +1,12 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import {AppDataService} from "@app/app-data.service"; @Injectable({ providedIn: 'root' }) export class CustomFileLoaderServiceService { + private appDataService = inject(AppDataService); - constructor(private appDataService: AppDataService) {} loadCustomFiles(): void { if (window.location.pathname === '/' && this.appDataService.public.node) { diff --git a/client/app/src/services/helper/receiver-tip.service.ts b/client/app/src/services/helper/receiver-tip.service.ts index 5f9610582a..f60dec0c16 100644 --- a/client/app/src/services/helper/receiver-tip.service.ts +++ b/client/app/src/services/helper/receiver-tip.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from "@angular/core"; +import { Injectable, inject } from "@angular/core"; import {HttpService} from "@app/shared/services/http.service"; import {AppDataService} from "@app/app-data.service"; import {RecieverTipData} from "@app/models/reciever/reciever-tip-data"; @@ -9,10 +9,11 @@ import { RedactionData } from "@app/models/component-model/redaction"; providedIn: "root" }) export class ReceiverTipService { - tip: RecieverTipData = new RecieverTipData(); + private httpService = inject(HttpService); + private appDataService = inject(AppDataService); + protected utils = inject(UtilsService); - constructor(private httpService: HttpService, private appDataService: AppDataService,protected utils: UtilsService) { - } + tip: RecieverTipData = new RecieverTipData(); reset() { this.tip = new RecieverTipData(); diff --git a/client/app/src/services/helper/resource-loader.service.ts b/client/app/src/services/helper/resource-loader.service.ts deleted file mode 100644 index 1ffab05d4d..0000000000 --- a/client/app/src/services/helper/resource-loader.service.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable({ - providedIn: 'root', -}) -export class ResourceLoaderService { - - // Function to dynamically load a stylesheet - loadStyle(href: string): void { - const link = document.createElement('link'); - link.rel = 'stylesheet'; - link.href = href; - document.head.appendChild(link); - } -} diff --git a/client/app/src/services/helper/submission.service.ts b/client/app/src/services/helper/submission.service.ts index 6a63a79ba5..375e6ae09a 100644 --- a/client/app/src/services/helper/submission.service.ts +++ b/client/app/src/services/helper/submission.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from "@angular/core"; +import { Injectable, inject } from "@angular/core"; import {AppDataService} from "@app/app-data.service"; import {HttpService} from "@app/shared/services/http.service"; import {Context} from "@app/models/reciever/reciever-tip-data"; @@ -8,6 +8,9 @@ import {submissionResourceModel} from "@app/models/whistleblower/submission-reso providedIn: "root", }) export class SubmissionService { + private httpService = inject(HttpService); + private appDataService = inject(AppDataService); + submission: submissionResourceModel = new submissionResourceModel(); context: Context; receivers: string[] = []; @@ -19,9 +22,6 @@ export class SubmissionService { uploads: { [key: string]: any }; private sharedData: Flow[] = []; - constructor(private httpService: HttpService, private appDataService: AppDataService) { - } - setContextReceivers(context_id: number) { this.context = this.appDataService.contexts_by_id[context_id]; diff --git a/client/app/src/services/helper/translation.service.ts b/client/app/src/services/helper/translation.service.ts index fc35749236..896208802a 100644 --- a/client/app/src/services/helper/translation.service.ts +++ b/client/app/src/services/helper/translation.service.ts @@ -1,5 +1,5 @@ import {BehaviorSubject} from 'rxjs'; -import {Inject, Injectable, Renderer2} from "@angular/core"; +import { Injectable, Renderer2, inject } from "@angular/core"; import {LangChangeEvent, TranslateService} from "@ngx-translate/core"; import {UtilsService} from "@app/shared/services/utils.service"; import {DOCUMENT} from "@angular/common"; @@ -8,6 +8,10 @@ import {DOCUMENT} from "@angular/common"; providedIn: "root", }) export class TranslationService { + private utilsService = inject(UtilsService); + protected translate = inject(TranslateService); + private document = inject(DOCUMENT); + language = ""; private currentLocale = new BehaviorSubject(""); @@ -19,10 +23,31 @@ export class TranslationService { public currentDirection: string; - constructor(private utilsService: UtilsService, protected translate: TranslateService, @Inject(DOCUMENT) private document: Document) { + constructor() { this.currentDirection = this.utilsService.getDirection(this.translate.currentLang); } + handleLTRandRTLStyling(event: LangChangeEvent, renderer: Renderer2) { + let waitForLoader = false; + const newDirection = this.utilsService.getDirection(event.lang); + if (newDirection !== this.currentDirection) { + waitForLoader = true; + this.utilsService.removeStyles(renderer, this.document, "css/styles-" + this.currentDirection + ".css"); + + const lang = this.translate.currentLang; + const cssFilename = ['ar', 'dv', 'fa', 'fa_AF', 'he', 'ps', 'ug', 'ur'].includes(lang) ? 'styles-rtl.css' : 'styles-ltr.css'; + const cssPath = `css/${cssFilename}`; + const newLinkElement = renderer.createElement('link'); + renderer.setAttribute(newLinkElement, 'rel', 'stylesheet'); + renderer.setAttribute(newLinkElement, 'type', 'text/css'); + renderer.setAttribute(newLinkElement, 'href', cssPath); + const firstLink = this.document.head.querySelector('link'); + renderer.insertBefore(this.document.head, newLinkElement, firstLink); + this.currentDirection = newDirection; + } + return waitForLoader; + } + onChange(changedLanguage: string, callback?: () => void) { this.language = changedLanguage; this.changeLocale(this.language); diff --git a/client/app/src/services/helper/wbtip.service.ts b/client/app/src/services/helper/wbtip.service.ts index 868f246d9f..108728d82d 100644 --- a/client/app/src/services/helper/wbtip.service.ts +++ b/client/app/src/services/helper/wbtip.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from "@angular/core"; +import { Injectable, inject } from "@angular/core"; import {HttpService} from "@app/shared/services/http.service"; import {Receiver, WbTipData} from "@app/models/whistleblower/wb-tip-data"; import {AppDataService} from "@app/app-data.service"; @@ -7,10 +7,10 @@ import {AppDataService} from "@app/app-data.service"; providedIn: "root" }) export class WbtipService { - tip: WbTipData = new WbTipData(); + private httpService = inject(HttpService); + private appDataService = inject(AppDataService); - constructor(private httpService: HttpService, private appDataService: AppDataService) { - } + tip: WbTipData = new WbTipData(); initialize(response: WbTipData) { this.tip = response; diff --git a/client/app/src/services/root/app-config.service.ts b/client/app/src/services/root/app-config.service.ts index 01956faefa..29a9ecf3e1 100644 --- a/client/app/src/services/root/app-config.service.ts +++ b/client/app/src/services/root/app-config.service.ts @@ -1,4 +1,4 @@ -import {Injectable, NgZone} from "@angular/core"; +import { Injectable, inject } from "@angular/core"; import {HttpService} from "@app/shared/services/http.service"; import {UtilsService} from "@app/shared/services/utils.service"; import {AppDataService} from "@app/app-data.service"; @@ -9,28 +9,33 @@ import {AuthenticationService} from "@app/services/helper/authentication.service import {LanguagesSupported} from "@app/models/app/public-model"; import {TitleService} from "@app/shared/services/title.service"; import {CustomFileLoaderServiceService} from "@app/services/helper/custom-file-loader-service.service"; +import {NgZone} from "@angular/core"; import {mockEngine} from "@app/services/helper/mocks"; @Injectable({ providedIn: "root" }) export class AppConfigService { + private customFileLoaderServiceService = inject(CustomFileLoaderServiceService); + private titleService = inject(TitleService); + authenticationService = inject(AuthenticationService); + private translationService = inject(TranslationService); + private utilsService = inject(UtilsService); + private router = inject(Router); + private activatedRoute = inject(ActivatedRoute); + private httpService = inject(HttpService); + private appDataService = inject(AppDataService); + private fieldUtilitiesService = inject(FieldUtilitiesService); + private ngZone = inject(NgZone); + private isRunning: boolean = false; + public sidebar: string = ""; private firstLoad = true; - private isRunning: boolean = false; - constructor(private ngZone: NgZone, private customFileLoaderServiceService: CustomFileLoaderServiceService, private titleService: TitleService, public authenticationService: AuthenticationService, private translationService: TranslationService, private utilsService: UtilsService, private router: Router, private activatedRoute: ActivatedRoute, private httpService: HttpService, private appDataService: AppDataService, private fieldUtilitiesService: FieldUtilitiesService) { + constructor() { this.init(); } - init() { - this.activatedRoute.paramMap.subscribe(_ => { - const currentURL = window.location.hash.substring(2).split("?")[0]; - this.initRoutes(currentURL); - this.localInitialization(); - }); - } - private monitorChangeDetection(): void { this.ngZone.onStable.subscribe(() => { if (this.isRunning) { @@ -45,6 +50,14 @@ export class AppConfigService { }); } + init() { + this.activatedRoute.paramMap.subscribe(_ => { + const currentURL = window.location.hash.substring(2).split("?")[0]; + this.initRoutes(currentURL); + this.localInitialization(); + }); + } + initRoutes(currentURL: string) { if (this.authenticationService && this.authenticationService.session && currentURL !== "login") { const queryParams = this.activatedRoute.snapshot.queryParams; @@ -185,9 +198,6 @@ export class AppConfigService { routeChangeListener() { this.router.events.subscribe((event) => { if (event instanceof NavigationEnd) { - if (event.url === '/') { - this.customFileLoaderServiceService.loadCustomFiles(); - } this.onValidateInitialConfiguration(); const lastChildRoute = this.findLastChildRoute(this.router.routerState.root); if (lastChildRoute && lastChildRoute.snapshot.data && lastChildRoute.snapshot.data["pageTitle"]) { diff --git a/client/app/src/services/root/app-interceptor.service.ts b/client/app/src/services/root/app-interceptor.service.ts index f961cab562..f560bdcbd6 100644 --- a/client/app/src/services/root/app-interceptor.service.ts +++ b/client/app/src/services/root/app-interceptor.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from "@angular/core"; +import { Injectable, inject } from "@angular/core"; import { HttpInterceptor, HttpEvent, @@ -28,8 +28,11 @@ const protectedUrls = [ @Injectable() export class appInterceptor implements HttpInterceptor { - constructor(private authenticationService: AuthenticationService, private httpClient: HttpClient, private cryptoService: CryptoService, private translationService: TranslationService) { - } + private authenticationService = inject(AuthenticationService); + private httpClient = inject(HttpClient); + private cryptoService = inject(CryptoService); + private translationService = inject(TranslationService); + private getAcceptLanguageHeader(): string | null { if (this.translationService.language) { @@ -85,9 +88,9 @@ export class appInterceptor implements HttpInterceptor { @Injectable() export class ErrorCatchingInterceptor implements HttpInterceptor { + private authenticationService = inject(AuthenticationService); + private appDataService = inject(AppDataService); - constructor(private authenticationService: AuthenticationService, private appDataService: AppDataService) { - } intercept(request: HttpRequest, next: HttpHandler): Observable> { @@ -114,10 +117,9 @@ export class ErrorCatchingInterceptor implements HttpInterceptor { @Injectable() export class CompletedInterceptor implements HttpInterceptor { - count = 0; + private appDataService = inject(AppDataService); - constructor(private appDataService: AppDataService) { - } + count = 0; intercept(req: HttpRequest, next: HttpHandler): Observable> { if (req.url !== "api/auth/authentication") { diff --git a/client/app/src/shared.module.ts b/client/app/src/shared.module.ts deleted file mode 100644 index a47bc1a446..0000000000 --- a/client/app/src/shared.module.ts +++ /dev/null @@ -1,261 +0,0 @@ -import {NgModule} from "@angular/core"; -import {CommonModule, NgOptimizedImage} from "@angular/common"; -import {FooterComponent} from "@app/shared/partials/footer/footer.component"; -import {ReceiptComponent} from "@app/shared/partials/receipt/receipt.component"; -import {FormsModule, ReactiveFormsModule} from "@angular/forms"; -import {TranslatorPipe} from "@app/shared/pipes/translate"; -import {Enable2fa} from "@app/shared/partials/enable-2fa/enable-2fa"; -import {TranslateModule} from "@ngx-translate/core"; -import {QRCodeModule} from "angularx-qrcode"; -import {PasswordChangeComponent} from "@app/shared/partials/password-change/password-change.component"; -import {PasswordMeterComponent} from "@app/shared/components/password-meter/password-meter.component"; -import { NgbPagination,NgbPaginationFirst,NgbPaginationLast,NgbPaginationNext,NgbPaginationNumber,NgbPaginationPages,NgbPaginationPrevious,NgbNav,NgbNavItem,NgbNavLink,NgbNavContent,NgbProgressbar,NgbDatepickerModule,NgbDropdownModule,NgbTooltipModule} from "@ng-bootstrap/ng-bootstrap"; -import {PrivacyBadgeComponent} from "@app/shared/partials/privacybadge/privacy-badge.component"; -import {MarkdownModule} from "ngx-markdown"; -import {StripHtmlPipe} from "@app/shared/pipes/strip-html.pipe"; -import {ReceiptValidatorDirective} from "@app/shared/directive/receipt-validator.directive"; -import {TipInfoComponent} from "@app/shared/partials/tip-info/tip-info.component"; -import {NgSelectModule} from "@ng-select/ng-select"; -import {TipAdditionalQuestionnaireInviteComponent} from "@app/shared/partials/tip-additional-questionnaire-invite/tip-additional-questionnaire-invite.component"; -import {TipFieldComponent} from "@app/shared/partials/tip-field/tip-field.component"; -import {TipFieldAnswerEntryComponent} from "@app/shared/partials/tip-field-answer-entry/tip-field-answer-entry.component"; -import {TipQuestionnaireAnswersComponent} from "@app/shared/partials/tip-questionnaire-answers/tip-questionnaire-answers.component"; -import {DatePipe} from "@app/shared/pipes/date.pipe"; -import {SplitPipe} from "@app/shared/pipes/split.pipe"; -import {TipFilesWhistleblowerComponent} from "@app/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component"; -import {WidgetWbFilesComponent} from "@app/shared/partials/widget-wbfiles/widget-wb-files.component"; -import {ByteFmtPipe} from "@app/shared/pipes/byte-fmt.pipe"; -import {RFileUploadButtonComponent} from "@app/shared/partials/rfile-upload-button/r-file-upload-button.component"; -import {RFileUploadStatusComponent} from "@app/shared/partials/rfile-upload-status/r-file-upload-status.component"; -import {TipCommentsComponent} from "@app/shared/partials/tip-comments/tip-comments.component"; -import {LimitToPipe} from "@app/shared/pipes/limit-to.pipe"; -import {OrderByPipe} from "@app/shared/pipes/order-by.pipe"; -import {TipReceiverListComponent} from "@app/shared/partials/tip-receiver-list/tip-receiver-list.component"; -import {FilterPipe} from "@app/shared/pipes/filter.pipe"; -import {FilterSearchPipe} from "@app/shared/pipes/filter-search.pipe"; -import {RequestSupportComponent} from "@app/shared/modals/request-support/request-support.component"; -import {NgxFlowModule} from "@flowjs/ngx-flow"; -import {RFilesUploadStatusComponent} from "@app/shared/partials/rfiles-upload-status/r-files-upload-status.component"; -import {NgFormChangeDirective} from "@app/shared/directive/ng-form-change.directive"; -import {WbFilesComponent} from "@app/shared/partials/wbfiles/wb-files.component"; -import {DisableCcpDirective} from "@app/shared/directive/disable-ccp.directive"; -import {SubdomainValidatorDirective} from "@app/shared/directive/subdomain-validator.directive"; -import {PasswordStrengthValidatorDirective} from "@app/shared/directive/password-strength-validator.directive"; -import {UserHomeComponent} from "@app/shared/partials/user-home/user-home.component"; -import {UserWarningsComponent} from "@app/shared/partials/user-warnings/user-warnings.component"; -import {GrantAccessComponent} from "@app/shared/modals/grant-access/grant-access.component"; -import {RevokeAccessComponent} from "@app/shared/modals/revoke-access/revoke-access.component"; -import {DeleteConfirmationComponent} from "@app/shared/modals/delete-confirmation/delete-confirmation.component"; -import {DateRangeSelectorComponent} from "@app/shared/components/date-selector/date-selector.component"; -import {PreferencesComponent} from "@app/shared/partials/preferences/preferences.component"; -import {PreferenceTab1Component} from "@app/shared/partials/preference-tabs/preference-tab1/preference-tab1.component"; -import {PreferenceTab2Component} from "@app/shared/partials/preference-tabs/preference-tab2/preference-tab2.component"; -import {Enable2faComponent} from "@app/shared/modals/enable2fa/enable2fa.component"; -import {EncryptionRecoveryKeyComponent} from "@app/shared/modals/encryption-recovery-key/encryption-recovery-key.component"; -import {ConfirmationWithPasswordComponent} from "@app/shared/modals/confirmation-with-password/confirmation-with-password.component"; -import {ConfirmationWith2faComponent} from "@app/shared/modals/confirmation-with2fa/confirmation-with2fa.component"; -import {TipOperationFileIdentityAccessRequestComponent} from "@app/shared/modals/tip-operation-file-identity-access-request/tip-operation-file-identity-access-request.ompoent"; -import {TipFilesReceiverComponent} from "@app/shared/partials/tip-files-receiver/tip-files-receiver.component"; -import {TipOperationSetReminderComponent} from "@app/shared/modals/tip-operation-set-reminder/tip-operation-set-reminder.component"; -import {TipOperationPostponeComponent} from "@app/shared/modals/tip-operation-postpone/tip-operation-postpone.component"; -import {FileViewComponent} from "@app/shared/modals/file-view/file-view.component"; -import {TipUploadWbFileComponent} from "@app/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component"; -import {ImageUploadDirective} from "@app/shared/directive/image-upload.directive"; -import {ImageUploadComponent} from "@app/shared/partials/image-upload/image-upload.component"; -import {EnableEncryptionComponent} from "@app/shared/modals/enable-encryption/enable-encryption.component"; -import {AdminFileComponent} from "@app/shared/partials/admin-file/admin-file.component"; -import {ConfirmationComponent} from "@app/shared/modals/confirmation/confirmation.component"; -import {QuestionnaireDuplicationComponent} from "@app/shared/modals/questionnaire-duplication/questionnaire-duplication.component"; -import {AddOptionHintComponent} from "@app/shared/modals/add-option-hint/add-option-hint.component"; -import {TriggerReceiverComponent} from "@app/shared/modals/trigger-receiver/trigger-receiver.component"; -import {AssignScorePointsComponent} from "@app/shared/modals/assign-score-points/assign-score-points.component"; -import {TipOperationFileIdentityAccessReplyComponent} from "@app/shared/modals/tip-operation-file-identity-access-reply/tip-operation-file-identity-access-reply.component"; -import {DemoComponent} from "@app/shared/partials/demo/demo.component"; -import {MessageConsoleComponent} from "@app/shared/partials/messageconsole/message-console.component"; -import {AcceptAgreementComponent} from "@app/shared/modals/accept-agreement/accept-agreement.component"; -import {DisclaimerComponent} from "@app/shared/modals/disclaimer/disclaimer.component"; -import {TransferAccessComponent} from "@app/shared/modals/transfer-access/transfer-access.component"; -import {BlankComponent} from "@app/shared/blank/blank.component"; -import {VoiceRecorderComponent} from "@app/shared/partials/voice-recorder/voice-recorder.component"; -import {Tab1Component} from "@app/pages/admin/settings/tab1/tab1.component"; -import {SwitchComponent} from "@app/shared/components/switch/switch.component"; -import {ChangeSubmissionStatusComponent} from "@app/shared/modals/change-submission-status/change-submission-status.component"; -import {ReopenSubmissionComponent} from "@app/shared/modals/reopen-submission/reopen-submission.component"; -import {OtkcAccessComponent} from "@app/shared/modals/otkc-access/otkc-access.component"; -import {OperationComponent} from "@app/shared/partials/operation/operation.component"; -import {RedactInformationComponent} from "@app/shared/modals/redact-information/redact-information.component"; - -@NgModule({ - imports: [ - CommonModule, - TranslateModule, - FormsModule, - QRCodeModule, - ReactiveFormsModule, - NgbProgressbar, - MarkdownModule, - NgSelectModule, - NgbPagination, - NgbPaginationPrevious, - NgbPaginationNext, - NgbPaginationFirst, - NgbPaginationLast, - NgbPaginationNumber, - NgbPaginationPages, - NgxFlowModule, - NgbDatepickerModule, - NgbDropdownModule, - DateRangeSelectorComponent, - NgbNav, - NgbNavItem, - NgbNavLink, - NgbNavContent, - NgbDropdownModule, - NgbTooltipModule, - NgOptimizedImage, - ], - declarations: [ - FooterComponent, - ReceiptComponent, - TranslatorPipe, - Enable2fa, - PasswordChangeComponent, - PasswordMeterComponent, - PrivacyBadgeComponent, - StripHtmlPipe, - DatePipe, - ReceiptValidatorDirective, - TipInfoComponent, - TipQuestionnaireAnswersComponent, - TipAdditionalQuestionnaireInviteComponent, - TipFieldComponent, - TipFieldAnswerEntryComponent, - DatePipe, - SplitPipe, - TipFilesWhistleblowerComponent, - WidgetWbFilesComponent, - ByteFmtPipe, - RFileUploadButtonComponent, - RFileUploadStatusComponent, - TipCommentsComponent, - LimitToPipe, - OrderByPipe, - TipReceiverListComponent, - FilterPipe, - FilterSearchPipe, - RequestSupportComponent, - RFilesUploadStatusComponent, - NgFormChangeDirective, - WbFilesComponent, - DisableCcpDirective, - SubdomainValidatorDirective, - PasswordStrengthValidatorDirective, - PasswordStrengthValidatorDirective, - ImageUploadDirective, - ImageUploadComponent, - UserHomeComponent, - UserWarningsComponent, - GrantAccessComponent, - RevokeAccessComponent, - DeleteConfirmationComponent, - PreferencesComponent, - PreferenceTab1Component, - PreferenceTab2Component, - Enable2faComponent, - EncryptionRecoveryKeyComponent, - ConfirmationWithPasswordComponent, - ConfirmationWith2faComponent, - TipOperationFileIdentityAccessRequestComponent, - TipFilesReceiverComponent, - TipOperationSetReminderComponent, - TipOperationPostponeComponent, - FileViewComponent, - TipUploadWbFileComponent, - EnableEncryptionComponent, - AdminFileComponent, - ConfirmationComponent, - QuestionnaireDuplicationComponent, - AddOptionHintComponent, - TriggerReceiverComponent, - AssignScorePointsComponent, - TipOperationFileIdentityAccessReplyComponent, - DemoComponent, - MessageConsoleComponent, - MessageConsoleComponent, - AcceptAgreementComponent, - TransferAccessComponent, - DisclaimerComponent, - ChangeSubmissionStatusComponent, - ReopenSubmissionComponent, - BlankComponent, - VoiceRecorderComponent, - Tab1Component, - SwitchComponent, - OtkcAccessComponent, - OperationComponent, - RedactInformationComponent - ], - exports: [ - FooterComponent, - ReceiptComponent, - TranslatorPipe, - PrivacyBadgeComponent, - Enable2fa, - PasswordChangeComponent, - StripHtmlPipe, - FilterPipe, - FilterSearchPipe, - OrderByPipe, - TipInfoComponent, - TipQuestionnaireAnswersComponent, - TipAdditionalQuestionnaireInviteComponent, - TipFieldComponent, - TipFilesWhistleblowerComponent, - WidgetWbFilesComponent, - TipCommentsComponent, - TipReceiverListComponent, - RFileUploadStatusComponent, - RFileUploadButtonComponent, - RFilesUploadStatusComponent, - NgFormChangeDirective, - DisableCcpDirective, - SubdomainValidatorDirective, - PasswordMeterComponent, - PasswordStrengthValidatorDirective, - ImageUploadDirective, - ImageUploadComponent, - UserHomeComponent, - UserWarningsComponent, - GrantAccessComponent, - RevokeAccessComponent, - DeleteConfirmationComponent, - DateRangeSelectorComponent, - TipOperationFileIdentityAccessRequestComponent, - TipFilesReceiverComponent, - TipOperationSetReminderComponent, - TipUploadWbFileComponent, - EnableEncryptionComponent, - AdminFileComponent, - ConfirmationComponent, - QuestionnaireDuplicationComponent, - AddOptionHintComponent, - TriggerReceiverComponent, - AssignScorePointsComponent, - PreferencesComponent, - DemoComponent, - MessageConsoleComponent, - AcceptAgreementComponent, - TransferAccessComponent, - DisclaimerComponent, - ChangeSubmissionStatusComponent, - ReopenSubmissionComponent, - VoiceRecorderComponent, - Tab1Component, - SwitchComponent, - OtkcAccessComponent, - OperationComponent, - RedactInformationComponent - ] -}) -export class SharedModule { -} diff --git a/client/app/src/shared/blank/blank.component.ts b/client/app/src/shared/blank/blank.component.ts index 9690988b37..a287a332ea 100644 --- a/client/app/src/shared/blank/blank.component.ts +++ b/client/app/src/shared/blank/blank.component.ts @@ -1,8 +1,9 @@ import {Component} from "@angular/core"; @Component({ - selector: "src-blank", - templateUrl: "./blank.component.html" + selector: "src-blank", + templateUrl: "./blank.component.html", + standalone: true }) export class BlankComponent { diff --git a/client/app/src/shared/components/password-meter/password-meter.component.ts b/client/app/src/shared/components/password-meter/password-meter.component.ts index 48b6035be1..64631aa8ce 100644 --- a/client/app/src/shared/components/password-meter/password-meter.component.ts +++ b/client/app/src/shared/components/password-meter/password-meter.component.ts @@ -1,8 +1,11 @@ import {Component, Input, OnChanges, SimpleChanges} from "@angular/core"; +import { NgClass } from "@angular/common"; @Component({ - selector: "src-password-meter", - templateUrl: "./password-meter.component.html" + selector: "src-password-meter", + templateUrl: "./password-meter.component.html", + standalone: true, + imports: [NgClass] }) export class PasswordMeterComponent implements OnChanges { diff --git a/client/app/src/shared/components/switch/switch.component.ts b/client/app/src/shared/components/switch/switch.component.ts index ef91cea44c..b51e93247a 100644 --- a/client/app/src/shared/components/switch/switch.component.ts +++ b/client/app/src/shared/components/switch/switch.component.ts @@ -1,8 +1,13 @@ import {Component, Input, Output, EventEmitter} from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { TranslateModule } from '@ngx-translate/core'; +import { TranslatorPipe } from '@app/shared/pipes/translate'; @Component({ - selector: 'app-switch', - templateUrl: './switch.component.html' + selector: 'app-switch', + templateUrl: './switch.component.html', + standalone: true, + imports: [FormsModule, TranslateModule, TranslatorPipe] }) export class SwitchComponent { @Input() label: string = 'Switch'; diff --git a/client/app/src/shared/directive/disable-ccp.directive.ts b/client/app/src/shared/directive/disable-ccp.directive.ts index c6c3fa0a78..b8f706f1a1 100644 --- a/client/app/src/shared/directive/disable-ccp.directive.ts +++ b/client/app/src/shared/directive/disable-ccp.directive.ts @@ -1,7 +1,8 @@ import {Directive, HostListener} from "@angular/core"; @Directive({ - selector: "[disableCcp]" + selector: "[disableCcp]", + standalone: true }) export class DisableCcpDirective { @HostListener("cut", ["$event"]) diff --git a/client/app/src/shared/directive/image-upload.directive.ts b/client/app/src/shared/directive/image-upload.directive.ts index fe7b81a711..d644325ed4 100644 --- a/client/app/src/shared/directive/image-upload.directive.ts +++ b/client/app/src/shared/directive/image-upload.directive.ts @@ -1,21 +1,22 @@ -import {ComponentFactoryResolver, Directive, Input, ViewContainerRef, OnInit} from "@angular/core"; +import { ComponentFactoryResolver, Directive, Input, ViewContainerRef, OnInit, inject } from "@angular/core"; import {contextResolverModel} from "@app/models/resolvers/context-resolver-model"; import {nodeResolverModel} from "@app/models/resolvers/node-resolver-model"; import {userResolverModel} from "@app/models/resolvers/user-resolver-model"; import {ImageUploadComponent} from "@app/shared/partials/image-upload/image-upload.component"; @Directive({ - selector: "[appImageUpload]", + selector: "[appImageUpload]", + standalone: true, }) export class ImageUploadDirective implements OnInit { + private viewContainerRef = inject(ViewContainerRef); + private componentFactoryResolver = inject(ComponentFactoryResolver); + @Input() imageUploadModel: contextResolverModel | nodeResolverModel | userResolverModel; @Input() imageUploadModelAttr: string; @Input() imageUploadId: string; @Input() imageSrcUrl: string; - constructor(private viewContainerRef: ViewContainerRef, private componentFactoryResolver: ComponentFactoryResolver) { - } - ngOnInit() { const componentFactory = this.componentFactoryResolver.resolveComponentFactory(ImageUploadComponent); const componentRef = this.viewContainerRef.createComponent(componentFactory); diff --git a/client/app/src/shared/directive/ng-form-change.directive.ts b/client/app/src/shared/directive/ng-form-change.directive.ts index d372e9ddea..2c0f50ed13 100644 --- a/client/app/src/shared/directive/ng-form-change.directive.ts +++ b/client/app/src/shared/directive/ng-form-change.directive.ts @@ -1,18 +1,18 @@ -import {Directive, EventEmitter, OnDestroy, OnInit, Output} from "@angular/core"; +import { Directive, EventEmitter, OnDestroy, OnInit, Output, inject } from "@angular/core"; import {debounceTime, Subscription} from "rxjs"; import {NgForm} from "@angular/forms"; @Directive({ - selector: "[ngFormChanges]" + selector: "[ngFormChanges]", + standalone: true }) export class NgFormChangeDirective implements OnInit, OnDestroy { + private ngForm = inject(NgForm); + @Output("ngFormChange") formChange: EventEmitter = new EventEmitter(); private formSubscription: Subscription; - constructor(private ngForm: NgForm) { - } - ngOnInit() { this.formSubscription = this.ngForm.form.valueChanges.pipe(debounceTime(150)).subscribe(() => { this.formChange.emit(); diff --git a/client/app/src/shared/directive/password-strength-validator.directive.ts b/client/app/src/shared/directive/password-strength-validator.directive.ts index ce151c509f..2b29e0ee96 100644 --- a/client/app/src/shared/directive/password-strength-validator.directive.ts +++ b/client/app/src/shared/directive/password-strength-validator.directive.ts @@ -2,12 +2,13 @@ import {Directive, Input, Output, EventEmitter} from "@angular/core"; import {NG_VALIDATORS, AbstractControl, Validator, ValidationErrors} from "@angular/forms"; @Directive({ - selector: "[passwordStrengthValidator]", - providers: [{ - provide: NG_VALIDATORS, - useExisting: PasswordStrengthValidatorDirective, - multi: true - }] + selector: "[passwordStrengthValidator]", + providers: [{ + provide: NG_VALIDATORS, + useExisting: PasswordStrengthValidatorDirective, + multi: true + }], + standalone: true }) export class PasswordStrengthValidatorDirective implements Validator { @Input("passwordStrengthValidator") passwordStrength: string; diff --git a/client/app/src/shared/directive/receipt-validator.directive.ts b/client/app/src/shared/directive/receipt-validator.directive.ts index 1495b32e35..5dd119266f 100644 --- a/client/app/src/shared/directive/receipt-validator.directive.ts +++ b/client/app/src/shared/directive/receipt-validator.directive.ts @@ -2,12 +2,13 @@ import {Directive, HostListener} from "@angular/core"; import {AbstractControl, Validator, NG_VALIDATORS, ValidationErrors} from "@angular/forms"; @Directive({ - selector: "[customReceiptValidator]", - providers: [{ - provide: NG_VALIDATORS, - useExisting: ReceiptValidatorDirective, - multi: true - }] + selector: "[customReceiptValidator]", + providers: [{ + provide: NG_VALIDATORS, + useExisting: ReceiptValidatorDirective, + multi: true + }], + standalone: true }) export class ReceiptValidatorDirective implements Validator { diff --git a/client/app/src/shared/directive/subdomain-validator.directive.ts b/client/app/src/shared/directive/subdomain-validator.directive.ts index 8ecfa6edfd..d6a00fdd43 100644 --- a/client/app/src/shared/directive/subdomain-validator.directive.ts +++ b/client/app/src/shared/directive/subdomain-validator.directive.ts @@ -1,10 +1,14 @@ -import {Directive, ElementRef, HostListener} from "@angular/core"; +import { Directive, ElementRef, HostListener, inject } from "@angular/core"; import {NgModel} from "@angular/forms"; @Directive({ - selector: "[subdomainvalidators]" + selector: "[subdomainvalidators]", + standalone: true }) export class SubdomainValidatorDirective { + private el = inject(ElementRef); + private ngModel = inject(NgModel); + @HostListener("input") onInput() { @@ -14,7 +18,4 @@ export class SubdomainValidatorDirective { this.el.nativeElement.value = viewValue; this.ngModel.update.emit(viewValue); } - - constructor(private el: ElementRef, private ngModel: NgModel) { - } } diff --git a/client/app/src/shared/guards/admin.guard.ts b/client/app/src/shared/guards/admin.guard.ts index a32cb75543..96df4c5792 100644 --- a/client/app/src/shared/guards/admin.guard.ts +++ b/client/app/src/shared/guards/admin.guard.ts @@ -1,4 +1,4 @@ -import {Injectable} from "@angular/core"; +import { Injectable, inject } from "@angular/core"; import {ActivatedRoute, Router, UrlTree} from "@angular/router"; import {Observable} from "rxjs"; import {AuthenticationService} from "@app/services/helper/authentication.service"; @@ -9,8 +9,12 @@ import {UtilsService} from "@app/shared/services/utils.service"; providedIn: "root" }) export class AdminGuard { - constructor(private activatedRoute: ActivatedRoute, private utilsService: UtilsService, private router: Router, private appConfigService: AppConfigService, public authenticationService: AuthenticationService) { - } + private activatedRoute = inject(ActivatedRoute); + private utilsService = inject(UtilsService); + private router = inject(Router); + private appConfigService = inject(AppConfigService); + authenticationService = inject(AuthenticationService); + canActivate(): Observable | Promise | boolean | UrlTree { if (this.authenticationService.session) { diff --git a/client/app/src/shared/guards/analyst.guard.ts b/client/app/src/shared/guards/analyst.guard.ts index d35b13df3e..a727f6d06f 100644 --- a/client/app/src/shared/guards/analyst.guard.ts +++ b/client/app/src/shared/guards/analyst.guard.ts @@ -1,4 +1,4 @@ -import {Injectable} from "@angular/core"; +import { Injectable, inject } from "@angular/core"; import {Router, UrlTree} from "@angular/router"; import {Observable} from "rxjs"; import {AuthenticationService} from "@app/services/helper/authentication.service"; @@ -9,8 +9,11 @@ import {UtilsService} from "@app/shared/services/utils.service"; providedIn: "root" }) export class AnalystGuard { - constructor(private utilsService: UtilsService, private appConfigService: AppConfigService, private router: Router, public authenticationService: AuthenticationService) { - } + private utilsService = inject(UtilsService); + private appConfigService = inject(AppConfigService); + private router = inject(Router); + authenticationService = inject(AuthenticationService); + canActivate(): Observable | Promise | boolean | UrlTree { if (this.authenticationService.session) { diff --git a/client/app/src/shared/guards/custodian.guard.ts b/client/app/src/shared/guards/custodian.guard.ts index 2c0875af82..7ddd57f22b 100644 --- a/client/app/src/shared/guards/custodian.guard.ts +++ b/client/app/src/shared/guards/custodian.guard.ts @@ -1,4 +1,4 @@ -import {Injectable} from "@angular/core"; +import { Injectable, inject } from "@angular/core"; import {Router, UrlTree} from "@angular/router"; import {Observable} from "rxjs"; import {AuthenticationService} from "@app/services/helper/authentication.service"; @@ -9,8 +9,11 @@ import {UtilsService} from "@app/shared/services/utils.service"; providedIn: "root" }) export class CustodianGuard { - constructor(private utilsService: UtilsService, private appConfigService: AppConfigService, private router: Router, public authenticationService: AuthenticationService) { - } + private utilsService = inject(UtilsService); + private appConfigService = inject(AppConfigService); + private router = inject(Router); + authenticationService = inject(AuthenticationService); + canActivate(): Observable | Promise | boolean | UrlTree { if (this.authenticationService.session) { diff --git a/client/app/src/shared/guards/pageguard.service.ts b/client/app/src/shared/guards/pageguard.service.ts index e37bad990b..e690857216 100644 --- a/client/app/src/shared/guards/pageguard.service.ts +++ b/client/app/src/shared/guards/pageguard.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from "@angular/core"; +import { Injectable, inject } from "@angular/core"; import {ActivatedRouteSnapshot, Router, RouterStateSnapshot, UrlTree} from "@angular/router"; import {Observable} from "rxjs"; import {AppDataService} from "@app/app-data.service"; @@ -8,8 +8,10 @@ import {AuthenticationService} from "@app/services/helper/authentication.service providedIn: "root" }) export class Pageguard { - constructor(private authenticationService: AuthenticationService, private router: Router, private appDataService: AppDataService) { - } + private authenticationService = inject(AuthenticationService); + private router = inject(Router); + private appDataService = inject(AppDataService); + canActivate(_: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { diff --git a/client/app/src/shared/guards/receiver.guard.ts b/client/app/src/shared/guards/receiver.guard.ts index f9edc68f7a..c7e8246917 100644 --- a/client/app/src/shared/guards/receiver.guard.ts +++ b/client/app/src/shared/guards/receiver.guard.ts @@ -1,4 +1,4 @@ -import {Injectable} from "@angular/core"; +import { Injectable, inject } from "@angular/core"; import {Router, UrlTree} from "@angular/router"; import {Observable} from "rxjs"; import {AuthenticationService} from "@app/services/helper/authentication.service"; @@ -9,14 +9,17 @@ import {UtilsService} from "@app/shared/services/utils.service"; providedIn: "root" }) export class ReceiverGuard { - constructor(private utilsService: UtilsService, private appConfigService: AppConfigService, private router: Router, public authenticationService: AuthenticationService) { - } + private utilsService = inject(UtilsService); + private appConfigService = inject(AppConfigService); + private router = inject(Router); + authenticationService = inject(AuthenticationService); + canActivate(): Observable | Promise | boolean | UrlTree { if (this.authenticationService.session) { if(this.authenticationService.session.role === "receiver"){ this.appConfigService.setPage(this.router.url); - } else { + }else { this.router.navigateByUrl("/login").then(); } return true; diff --git a/client/app/src/shared/modals/accept-agreement/accept-agreement.component.html b/client/app/src/shared/modals/accept-agreement/accept-agreement.component.html index ac78bf7a44..7291f094b7 100644 --- a/client/app/src/shared/modals/accept-agreement/accept-agreement.component.html +++ b/client/app/src/shared/modals/accept-agreement/accept-agreement.component.html @@ -1,19 +1,21 @@