diff --git a/alfa-client/apps/admin/src/app/app.component.html b/alfa-client/apps/admin/src/app/app.component.html index 6409e8398c47509d3cfa668979eb5d0e1cd91b26..2bec4bfa7e7bdac55e21d1a7ab5a645ece953f28 100644 --- a/alfa-client/apps/admin/src/app/app.component.html +++ b/alfa-client/apps/admin/src/app/app.component.html @@ -41,7 +41,7 @@ <div class="flex h-screen w-full justify-center overflow-y-auto"> <ods-navbar data-test-id="navigation"> @if (apiRoot | hasLink: apiRootLinkRel.USERS) { - <ods-nav-item data-test-id="users-roles-navigation" caption="Benutzer & Rollen" path="/benutzer_und_rollen"> + <ods-nav-item data-test-id="users-roles-navigation" caption="Benutzer & Rollen" [path]="routes.BENUTZER_UND_ROLLEN"> <ods-users-icon class="stroke-text" icon /> </ods-nav-item> } @@ -49,7 +49,7 @@ <ods-nav-item data-test-id="organisations-einheiten-navigation" caption="Organisationseinheiten" - path="/organisationseinheiten" + [path]="routes.ORGANISATIONSEINHEITEN" > <ods-orga-unit-icon icon /> </ods-nav-item> diff --git a/alfa-client/apps/admin/src/app/app.component.spec.ts b/alfa-client/apps/admin/src/app/app.component.spec.ts index 7789cebe72549c662115feb3c1162c5346b5e4a0..c9fc4300b0ad4d7d7355e475f7693ebde70e5090 100644 --- a/alfa-client/apps/admin/src/app/app.component.spec.ts +++ b/alfa-client/apps/admin/src/app/app.component.spec.ts @@ -22,32 +22,25 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { ConfigurationLinkRel, ConfigurationResource, ConfigurationService } from '@admin-client/configuration-shared'; +import { ROUTES } from '@admin-client/shared'; import { KeycloakTokenService } from '@admin/keycloak-shared'; import { ApiRootLinkRel, ApiRootResource, ApiRootService } from '@alfa-client/api-root-shared'; import { BuildInfoComponent } from '@alfa-client/common'; -import { HasLinkPipe, createEmptyStateResource, createStateResource } from '@alfa-client/tech-shared'; -import { Mock, existsAsHtmlElement, mock, notExistsAsHtmlElement } from '@alfa-client/test-utils'; +import { createEmptyStateResource, createStateResource, HasLinkPipe } from '@alfa-client/tech-shared'; +import { existsAsHtmlElement, getElementComponentFromFixtureByCss, Mock, mock, notExistsAsHtmlElement, } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { AuthenticationService } from '@authentication'; -import { - AdminLogoIconComponent, - MailboxIconComponent, - NavItemComponent, - NavbarComponent, - OrgaUnitIconComponent, - UsersIconComponent, -} from '@ods/system'; +import { AdminLogoIconComponent, MailboxIconComponent, NavbarComponent, NavItemComponent, OrgaUnitIconComponent, UsersIconComponent, } from '@ods/system'; import { createConfigurationResource } from 'libs/admin/configuration-shared/test/configuration'; import { MenuContainerComponent } from 'libs/admin/configuration/src/lib/menu-container/menu-container.component'; import { createApiRootResource } from 'libs/api-root-shared/test/api-root'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { MockComponent, MockDirective } from 'ng-mocks'; -import { Subscription, of } from 'rxjs'; +import { of, Subscription } from 'rxjs'; import { UserProfileButtonContainerComponent } from '../common/user-profile-button-container/user-profile.button-container.component'; import { UnavailablePageComponent } from '../pages/unavailable/unavailable-page/unavailable-page.component'; import { AppComponent } from './app.component'; -import exp = require('constants'); describe('AppComponent', () => { let component: AppComponent; @@ -381,6 +374,20 @@ describe('AppComponent', () => { describe('navigation', () => { describe('user and roles', () => { + it('should have inputs', () => { + component.apiRootStateResource$ = of( + createStateResource(createApiRootResource([ApiRootLinkRel.CONFIGURATION, ApiRootLinkRel.USERS])), + ); + + fixture.detectChanges(); + const naviItemComponent: NavItemComponent = getElementComponentFromFixtureByCss<NavItemComponent>( + fixture, + usersRolesNavigationSelector, + ); + + expect(naviItemComponent.path).toEqual(ROUTES.BENUTZER_UND_ROLLEN); + }); + it('should show if users link is present', () => { component.apiRootStateResource$ = of( createStateResource(createApiRootResource([ApiRootLinkRel.CONFIGURATION, ApiRootLinkRel.USERS])), @@ -401,6 +408,20 @@ describe('AppComponent', () => { }); describe('organisationEinheiten', () => { + it('should have inputs', () => { + component.apiRootStateResource$ = of( + createStateResource(createApiRootResource([ApiRootLinkRel.ORGANISATIONS_EINHEIT, ApiRootLinkRel.CONFIGURATION])), + ); + + fixture.detectChanges(); + const naviItemComponent: NavItemComponent = getElementComponentFromFixtureByCss<NavItemComponent>( + fixture, + organisationsEinheitenNavigationSelector, + ); + + expect(naviItemComponent.path).toEqual(ROUTES.ORGANISATIONSEINHEITEN); + }); + it('should show if link in apiRoot exists', () => { component.apiRootStateResource$ = of( createStateResource(createApiRootResource([ApiRootLinkRel.ORGANISATIONS_EINHEIT, ApiRootLinkRel.CONFIGURATION])), diff --git a/alfa-client/apps/admin/src/app/app.component.ts b/alfa-client/apps/admin/src/app/app.component.ts index a523590d12806227b1aad8f7b1fca8b4d0a996eb..3a5d98dce6cdf1dc185a64765f03da9514a8f86e 100644 --- a/alfa-client/apps/admin/src/app/app.component.ts +++ b/alfa-client/apps/admin/src/app/app.component.ts @@ -33,13 +33,7 @@ import { Component, inject, OnInit } from '@angular/core'; import { Router, RouterLink, RouterOutlet } from '@angular/router'; import { AuthenticationService } from '@authentication'; import { hasLink } from '@ngxp/rest'; -import { - AdminLogoIconComponent, - NavbarComponent, - NavItemComponent, - OrgaUnitIconComponent, - UsersIconComponent, -} from '@ods/system'; +import { AdminLogoIconComponent, NavbarComponent, NavItemComponent, OrgaUnitIconComponent, UsersIconComponent, } from '@ods/system'; import { filter, Observable, Subscription } from 'rxjs'; import { UserProfileButtonContainerComponent } from '../common/user-profile-button-container/user-profile.button-container.component'; import { UnavailablePageComponent } from '../pages/unavailable/unavailable-page/unavailable-page.component'; @@ -80,6 +74,7 @@ export class AppComponent implements OnInit { configurationSubscription: Subscription; public readonly apiRootLinkRel = ApiRootLinkRel; + public readonly routes = ROUTES; ngOnInit(): void { this.authenticationService.login().then(() => this.doAfterLoggedIn()); diff --git a/alfa-client/apps/admin/src/main.spec.ts b/alfa-client/apps/admin/src/main.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..e5d99078fea75d75fbaf9737ba07bb7d5dbf3d09 --- /dev/null +++ b/alfa-client/apps/admin/src/main.spec.ts @@ -0,0 +1,18 @@ +import { TestBed } from '@angular/core/testing'; +import { ActivatedRouteSnapshot, RouteReuseStrategy } from '@angular/router'; +import { NoRouteReuseStrategy } from './main'; + +describe('route reuse strategy', () => { + let strategy: RouteReuseStrategy; + beforeEach(() => { + TestBed.configureTestingModule({ providers: [{ provide: RouteReuseStrategy, useClass: NoRouteReuseStrategy }] }); + + strategy = TestBed.inject(RouteReuseStrategy); + }); + + describe('shouldReuseRoute', () => { + it('should return false', () => { + expect(strategy.shouldReuseRoute(new ActivatedRouteSnapshot(), new ActivatedRouteSnapshot())).toEqual(false); + }); + }); +}); diff --git a/alfa-client/apps/admin/src/main.ts b/alfa-client/apps/admin/src/main.ts index 08c45af4a7d2e8fd7ffbf73de54114c7b45a18e7..517ab0538bea573be67b1b8184ec7bb50695c242 100644 --- a/alfa-client/apps/admin/src/main.ts +++ b/alfa-client/apps/admin/src/main.ts @@ -22,7 +22,7 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { EnvironmentModule, loadEnvironment } from '@alfa-client/environment-shared'; -import { enableProdMode, importProvidersFrom } from '@angular/core'; +import { enableProdMode, importProvidersFrom, Injectable } from '@angular/core'; import { isNil } from 'lodash-es'; import { ApiRootModule } from '@alfa-client/api-root-shared'; @@ -30,15 +30,15 @@ import { NavigationSharedModule } from '@alfa-client/navigation-shared'; import { registerLocaleData } from '@angular/common'; import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import localeDe from '@angular/common/locales/de'; -import { BrowserModule, bootstrapApplication } from '@angular/platform-browser'; +import { bootstrapApplication, BrowserModule } from '@angular/platform-browser'; import { provideAnimations } from '@angular/platform-browser/animations'; -import { provideRouter } from '@angular/router'; +import { ActivatedRouteSnapshot, BaseRouteReuseStrategy, provideRouter, RouteReuseStrategy, withRouterConfig, } from '@angular/router'; +import { HttpUnauthorizedInterceptor } from '@authentication'; import { EffectsModule } from '@ngrx/effects'; import { StoreRouterConnectingModule } from '@ngrx/router-store'; import { StoreModule } from '@ngrx/store'; import { StoreDevtoolsModule } from '@ngrx/store-devtools'; import { OAuthModule } from 'angular-oauth2-oidc'; -import { HttpUnauthorizedInterceptor } from '@authentication'; import { ConfigurationsProviders } from 'libs/admin/configuration-shared/src/lib/configuration.providers'; import { PostfachProviders } from 'libs/admin/postfach-shared/src/lib/postfach.providers'; import { SettingsProviders } from 'libs/admin/settings-shared/src/lib/settings.providers'; @@ -49,6 +49,13 @@ import { environment } from './environments/environment'; registerLocaleData(localeDe); +@Injectable() +export class NoRouteReuseStrategy extends BaseRouteReuseStrategy { + public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { + return false; + } +} + loadEnvironment(environment.environmentUrl).then((env) => { if (isNil(env?.production)) { console.error('Connection Error: environment is ', env); @@ -61,6 +68,7 @@ loadEnvironment(environment.environmentUrl).then((env) => { bootstrapApplication(AppComponent, { providers: [ + { provide: RouteReuseStrategy, useClass: NoRouteReuseStrategy }, ConfigurationsProviders, PostfachProviders, SettingsProviders, @@ -86,7 +94,7 @@ loadEnvironment(environment.environmentUrl).then((env) => { multi: true, }, provideHttpClient(withInterceptorsFromDi()), - provideRouter(appRoutes), + provideRouter(appRoutes, withRouterConfig({ onSameUrlNavigation: 'reload' })), provideAnimations(), ], }).catch((err) => console.error(err)); diff --git a/alfa-client/apps/alfa-e2e/src/components/user-profile/current-user-profile.component.e2e.ts b/alfa-client/apps/alfa-e2e/src/components/user-profile/current-user-profile.component.e2e.ts index 93a38796fd04ecade67ecf9995a417a73ce687ba..8da3998b68e7f9f8b2604ae678813181885666f7 100644 --- a/alfa-client/apps/alfa-e2e/src/components/user-profile/current-user-profile.component.e2e.ts +++ b/alfa-client/apps/alfa-e2e/src/components/user-profile/current-user-profile.component.e2e.ts @@ -47,10 +47,6 @@ export class CurrentUserProfileE2EComponent { return cy.getTestElement(this.locatorUserIconButton); } - public getDropdownButton() { - return this.getRoot().getTestElement(this.dropdownButton); - } - public getLogoutButton() { return cy.getTestElement(this.locatorLogoutButton); } diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/accessibility/vorgang-list.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/accessibility/vorgang-list.cy.ts index 47c1fa4753627a6ddf1a3b48eca53a3133f2f2db..a254ba3a092baa3b88fa2579ecf0399e5a83029c 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/accessibility/vorgang-list.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/accessibility/vorgang-list.cy.ts @@ -106,7 +106,7 @@ describe('VorgangList Page', () => { it('should focus user icon', () => { pressTab(); - isOdsFocused(header.getCurrentUserProfile().getDropdownButton()); + isOdsFocused(header.getCurrentUserProfile().getUserIconButton()); }); }); diff --git a/alfa-client/apps/info/project.json b/alfa-client/apps/info/project.json index 6a94515647a2d9bac320d3c677a7a2393a790924..38c9788b9fbcaf060d509ed7ade48dcad8503c99 100644 --- a/alfa-client/apps/info/project.json +++ b/alfa-client/apps/info/project.json @@ -8,25 +8,16 @@ "targets": { "build": { "executor": "@angular-devkit/build-angular:browser-esbuild", - "outputs": [ - "{options.outputPath}" - ], + "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/apps/info", "index": "apps/info/src/index.html", "main": "apps/info/src/main.ts", - "polyfills": [ - "zone.js" - ], + "polyfills": ["zone.js"], "tsConfig": "apps/info/tsconfig.app.json", "inlineStyleLanguage": "scss", - "assets": [ - "apps/info/src/favicon.svg", - "apps/info/src/assets" - ], - "styles": [ - "apps/info/src/styles.scss" - ], + "assets": ["apps/info/src/favicon.svg", "apps/info/src/assets"], + "styles": ["apps/info/src/styles.scss"], "scripts": [], "stylePreprocessorOptions": { "includePaths": [ @@ -40,6 +31,10 @@ "configurations": { "production-by": { "fileReplacements": [ + { + "replace": "apps/info/src/environments/environment.ts", + "with": "apps/info/src/environments/environment.prod.by.ts" + }, { "replace": "apps/info/src/pages/accessibility/accessibility-page.component.ts", "with": "apps/info/src/pages/accessibility/accessibility-page-by.component.ts" @@ -61,9 +56,17 @@ }, "production-sh": { "fileReplacements": [ + { + "replace": "apps/info/src/environments/environment.ts", + "with": "apps/info/src/environments/environment.prod.sh.ts" + }, { "replace": "apps/info/src/pages/accessibility/accessibility-page.component.ts", "with": "apps/info/src/pages/accessibility/accessibility-page-sh.component.ts" + }, + { + "replace": "apps/info/src/pages/impressum/impressum-page.component.ts", + "with": "apps/info/src/pages/impressum/impressum-page-sh.component.ts" } ], "budgets": [ @@ -85,6 +88,10 @@ { "replace": "apps/info/src/pages/accessibility/accessibility-page.component.ts", "with": "apps/info/src/pages/accessibility/accessibility-page-sh.component.ts" + }, + { + "replace": "apps/info/src/pages/impressum/impressum-page.component.ts", + "with": "apps/info/src/pages/impressum/impressum-page-sh.component.ts" } ], "buildOptimizer": false, @@ -100,8 +107,11 @@ "serve": { "executor": "@angular-devkit/build-angular:dev-server", "configurations": { - "production": { - "buildTarget": "info:build:production" + "production-by": { + "buildTarget": "info:build:production-by" + }, + "production-sh": { + "buildTarget": "info:build:production-sh" }, "development": { "buildTarget": "info:build:development" @@ -120,9 +130,7 @@ }, "test": { "executor": "@nx/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/{projectRoot}" - ], + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], "options": { "jestConfig": "apps/info/jest.config.ts" } @@ -137,22 +145,16 @@ }, "container": { "executor": "@nx-tools/nx-container:build", - "dependsOn": [ - "build" - ], + "dependsOn": ["build"], "options": { "engine": "docker", "push": true, "metadata": { - "images": [ - "docker.ozg-sh.de/info" - ], + "images": ["docker.ozg-sh.de/info"], "load": true, - "tags": [ - "snapshot-latest" - ] + "tags": ["snapshot-latest"] } } } } -} \ No newline at end of file +} diff --git a/alfa-client/apps/info/src/app/app.component.html b/alfa-client/apps/info/src/app/app.component.html index 13d01813a0e5b8b42f74d308d648b60e2377dffc..b30e793d253d6e07873b825df8c247f8b69e11c6 100644 --- a/alfa-client/apps/info/src/app/app.component.html +++ b/alfa-client/apps/info/src/app/app.component.html @@ -11,4 +11,18 @@ <main class="flex-auto bg-background-50"> <router-outlet></router-outlet> </main> + @if (!isBayern) { + <footer class="ozg-prose flex justify-center gap-4" data-test-id="navigation-footer"> + <nav> + <ul class="flex flex-wrap justify-center gap-9"> + <li> + <a routerLink="/barrierefreiheit">Barrierefreiheit</a> + </li> + <li> + <a routerLink="/impressum">Impressum</a> + </li> + </ul> + </nav> + </footer> + } </div> diff --git a/alfa-client/apps/info/src/app/app.component.spec.ts b/alfa-client/apps/info/src/app/app.component.spec.ts index 5928900f952dcd5521fdafae4f2b042fb6be8b95..a9aef32a46554df9f598a3cbf086e713049d76d7 100644 --- a/alfa-client/apps/info/src/app/app.component.spec.ts +++ b/alfa-client/apps/info/src/app/app.component.spec.ts @@ -1,11 +1,15 @@ +import { existsAsHtmlElement, notExistsAsHtmlElement } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { provideRouter } from '@angular/router'; +import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { AppComponent } from './app.component'; describe('AppComponent', () => { let component: AppComponent; let fixture: ComponentFixture<AppComponent>; + const navigationLocator: string = getDataTestIdOf('navigation-footer'); + beforeEach(async () => { await TestBed.configureTestingModule({ imports: [AppComponent], @@ -20,4 +24,34 @@ describe('AppComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + describe('component', () => { + describe('ngOnInit', () => { + it('should set isBayern', () => { + component.ngOnInit(); + + expect(component.isBayern).toBe(false); + }); + }); + }); + + describe('template', () => { + describe('bundesland bayern', () => { + it('should NOT show navigation footer', () => { + component.isBayern = true; + + fixture.detectChanges(); + + notExistsAsHtmlElement(fixture, navigationLocator); + }); + + it('should show navigation footer', () => { + component.isBayern = false; + + fixture.detectChanges(); + + existsAsHtmlElement(fixture, navigationLocator); + }); + }); + }); }); diff --git a/alfa-client/apps/info/src/app/app.component.ts b/alfa-client/apps/info/src/app/app.component.ts index e81b19bbe0ad9e0ec1594bf7aa6f8c77b3801117..65ebf90d0dde07e83be8a3fe3981eb46e0c69987 100644 --- a/alfa-client/apps/info/src/app/app.component.ts +++ b/alfa-client/apps/info/src/app/app.component.ts @@ -1,6 +1,7 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { RouterModule } from '@angular/router'; import { OzgLogoIconComponent } from '@ods/system'; +import { environment } from '../environments/environment'; @Component({ standalone: true, @@ -8,4 +9,10 @@ import { OzgLogoIconComponent } from '@ods/system'; selector: 'app-root', templateUrl: './app.component.html', }) -export class AppComponent {} +export class AppComponent implements OnInit { + public isBayern: boolean; + + ngOnInit() { + this.isBayern = environment.bundesland === 'by'; + } +} diff --git a/alfa-client/apps/info/src/app/app.routes.ts b/alfa-client/apps/info/src/app/app.routes.ts index 2260ea6873811a57c40ea24dada72e8ef52a357c..d6ae093054d1fc56814c24e4b16c24b0e1705734 100644 --- a/alfa-client/apps/info/src/app/app.routes.ts +++ b/alfa-client/apps/info/src/app/app.routes.ts @@ -1,5 +1,6 @@ import { Route } from '@angular/router'; import { AccessibilityPageComponent } from '../pages/accessibility/accessibility-page.component'; +import { ImpressumPageComponent } from '../pages/impressum/impressum-page.component'; export const appRoutes: Route[] = [ { @@ -12,4 +13,10 @@ export const appRoutes: Route[] = [ component: AccessibilityPageComponent, title: 'Barrierefreiheit', }, + { + path: 'impressum', + component: ImpressumPageComponent, + title: 'Impressum', + }, + { path: '**', redirectTo: 'barrierefreiheit' }, ]; diff --git a/alfa-client/apps/info/src/environments/environment.prod.by.ts b/alfa-client/apps/info/src/environments/environment.prod.by.ts new file mode 100644 index 0000000000000000000000000000000000000000..5197eb27edcaa967af00f66b3b071dce2e042ca8 --- /dev/null +++ b/alfa-client/apps/info/src/environments/environment.prod.by.ts @@ -0,0 +1,4 @@ +export const environment = { + production: true, + bundesland: 'by', +}; diff --git a/alfa-client/apps/info/src/environments/environment.prod.sh.ts b/alfa-client/apps/info/src/environments/environment.prod.sh.ts new file mode 100644 index 0000000000000000000000000000000000000000..211c1eabf6a11fa3bfa93e5cc2c5d5fbaaf56ea1 --- /dev/null +++ b/alfa-client/apps/info/src/environments/environment.prod.sh.ts @@ -0,0 +1,4 @@ +export const environment = { + production: true, + bundesland: 'sh', +}; diff --git a/alfa-client/apps/info/src/environments/environment.ts b/alfa-client/apps/info/src/environments/environment.ts new file mode 100644 index 0000000000000000000000000000000000000000..879a4bf8ab755c728fb22c3d6655cdaa828180b2 --- /dev/null +++ b/alfa-client/apps/info/src/environments/environment.ts @@ -0,0 +1,4 @@ +export const environment = { + production: false, + bundesland: 'sh', +}; diff --git a/alfa-client/apps/info/src/pages/accessibility/accessibility-page-sh.component.ts b/alfa-client/apps/info/src/pages/accessibility/accessibility-page-sh.component.ts index 495f077f13aad80e28c0f1d287a62f3b08b8c2c1..995485dfe07560ee6fca49c97df9b1edb7f88089 100644 --- a/alfa-client/apps/info/src/pages/accessibility/accessibility-page-sh.component.ts +++ b/alfa-client/apps/info/src/pages/accessibility/accessibility-page-sh.component.ts @@ -8,46 +8,63 @@ import { Component } from '@angular/core'; template: `<div class="ozg-prose prose prose-h1:text-4xl prose-h2:text-2xl"> <h1>Erklärung zur Barrierefreiheit</h1> <p> - Die Staatskanzlei des Ministerpräsidenten ist bemüht, ihre Anwendung im Einklang mit § 11 Absatz 1 - Landesbehindertengleichstellungsgesetz (LBGG) sowie den Anforderungen der Barrierefreiheit gemäß § 13 Absatz 3 LBGG - barrierefrei zugänglich zu machen. + Die Staatskanzlei des Ministerpräsidenten ist bemüht, ihre Anwendung im Einklang mit den Anforderungen der Barrierefreiheit + nach § 12 Landesbehindertengleichstellungsgesetz (LBGG) barrierefrei zugänglich zu gestalten. Das LBGG verweist dabei auf + den Artikel 4 der Richtlinie (EU) 2016/2102. Demnach gilt es, die Europäische Norm EN 301 549 einzuhalten, wonach die + Erfolgskriterien der Web Content Accessibility Guidelines (WCAG) 2.1 in Konformitätsstufe AA zu erfüllen sind. </p> <p> Diese Erklärung zur Barrierefreiheit gilt für Unterseiten und Subdomains von <a href="https://kop.schleswig-holstein.de">https://kop.schleswig-holstein.de</a> </p> <h2>Stand der Vereinbarkeit mit den Anforderungen</h2> - <p>Diese Website/mobile Anwendung ist teilweise mit § 13 Absatz 3 LBGG vereinbar.</p> + <p>Diese Website/mobile Anwendung ist teilweise mit § 12 LBGG vereinbar.</p> <p> Die Anwendung befindet sich derzeit noch in der Entwicklung. An bestehenden und bekannten Barrieren wird gearbeitet, um eine - Barrierefreiheit gemäß § 13 Absatz 3 LBGG gewährleisten zu können. + Barrierefreiheit gemäß § 12 LBGG gewährleisten zu können. </p> + + <h3>Zeitraum der Behebung</h3> + <p>Die Maßnahmen zur Erfüllung der Barrierefreiheitsanforderungen werden in 2025 eingeplant.</p> + <h2>Allgemeiner Bereich</h2> <h3>Nicht barrierefreie Inhalte</h3> - <p>Die nachstehend aufgeführten Inhalte sind unvereinbar mit § 13 Absatz 3 LBGG und somit nicht barrierefrei:</p> + <p>Die nachstehend aufgeführten Inhalte sind unvereinbar mit § 12 LBGG und somit nicht barrierefrei:</p> <ul> - <li>Erläuterungen zu den Inhalten und der Navigation dieser Anwendung in leichter Sprache sind nicht vorhanden.</li> <li> - Responsive Darstellung ist nicht vorhanden, weshalb Inhalte nur bedingt verlustfrei vergrößerbar sind oder umbrechen. + Erläuterungen zu den Inhalten und der Navigation dieser Anwendung in Gebärdensprache und leichter Sprache sind nicht + vorhanden. </li> <li>Gliedernde Überschriften und Bereiche sind nicht umfassend vorhanden.</li> - <li>Benutzerdefinierte Einstellungen (Farbauswahl und Schriftgröße) werden nicht umfänglich angewendet.</li> - <li>Die Einstellungsmöglichkeiten im oberen Navigationsmenü können nicht per Tastatursteuerung erreicht werden.</li> - <li>Die Vorschläge der Vorgangssuche sind nicht per Screenreader wahrnehmbar.</li> <li>Vereinzelt treten Texte mit zu geringem Kontrast zum Hintergrund auf.</li> <li>Vereinzelt ist der Tastaturfokus nur schwer wahrnehmbar. Bei Datumspickern ist keine Fokushervorhebung vorhanden.</li> + <li>An diversen Bedienelementen fehlt ein aussagekräftiger Alternativtext.</li> <li> - Es fehlen Statusmeldungen, die Screenreader-Nutzende über Änderungen der Suchergebnisse oder über Änderungen von Anträgen - informieren. + Es fehlen Statusmeldungen, die Screenreader-Nutzende über fehlende Einträge der Suchergebnisse oder über Änderungen von + Anträgen informieren. Im Bereich des Bescheidens fehlen zudem auch sichtbare Statusmeldungen mit Auskunft über einen + erfolgreichen Upload oder einer automatischen Bescheiderstellung als PDF. </li> <li> - Der als PDF zur Verfügung gestellte Benutzerleitfaden, sowie die als PDF abgespeicherten Nachrichten der Anwendung - entsprechen nicht dem PDF/UA-Standard. + Im Bereich der Uploadbuttons fehlt eine Kennzeichnung dieser als Pflichtfeld und eine direkt sichtbare Fehlermeldung. Hier + fehlen auch sichtbare Hinweistexte zu Dateigröße und -format. + </li> + <li> + E-Mail-Adressen sind nicht per Tastatur ansteuerbar. Aus Sicherheitsgründen können E-Mail-Adressen in dieser Anwendung + nicht interaktiv gesetzt werden. + </li> + <li>Die Filtermöglichkeit im Bereich „Alle Vorgänge“ ist für Screenreader-Nutzende nicht gut wahrnehmbar.</li> + <li> + Der durch die Anwendung als PDF zur Verfügung gestellte Benutzerleitfaden, sowie die durch die Anwendung als PDF + abgespeicherten Nachrichten entsprechen nicht dem PDF/UA-Standard. + </li> + <li> + Die PDFs der automatischen Bescheiderstellung sind für Screenreader-Nutzende aufgrund diverser Mängel nicht wahrnehmbar. + Die Erstellung barrierefreier PDF-Bescheide obliegt jedoch den erstellenden Ämtern. </li> </ul> <h2>Administrationsbereich</h2> <h3>Nicht barrierefreie Inhalte</h3> - <p>Die nachstehend aufgeführten Inhalte sind unvereinbar mit § 13 Absatz 3 LBGG und somit nicht barrierefrei:</p> + <p>Die nachstehend aufgeführten Inhalte sind unvereinbar mit § 12 LBGG und somit nicht barrierefrei:</p> <ul> <li>Erläuterungen zu den Inhalten und der Navigation dieser Anwendung in leichter Sprache sind nicht vorhanden.</li> <li> @@ -58,12 +75,16 @@ import { Component } from '@angular/core'; <li>Der Tastaturfokus nur schwer wahrnehmbar.</li> </ul> <h2>Erstellung dieser Erklärung zur Barrierefreiheit</h2> - <p>Diese Erklärung wurde am 15.07.2024 erstellt.</p> + <p>Diese Erklärung wurde am 15.07.2024 erstellt und für den allgemeinen Bereich am 26.01.2025 aktualisiert.</p> + <p> + Die Aussagen bezüglich der Vereinbarkeit mit den Barrierefreiheitsanforderungen in dieser Erklärung zum + <strong>allgemeinen Bereich</strong> beruhen auf einem Prüfbericht von Dataport vom 14.12.2024 und einer Selbstbewertung. + </p> <p> - Die Aussagen bezüglich der Vereinbarkeit mit den Barrierefreiheitsanforderungen in dieser Erklärung beruhen auf einem - Prüfbericht von Dataport vom 08.04.2024 und einer Selbstbewertung. + Die Aussagen bezüglich der Vereinbarkeit mit den Barrierefreiheitsanforderungen in dieser Erklärung zum + <strong>Administrationsbereich</strong> beruhen auf einem Prüfbericht von Dataport vom 08.04.2024 und einer Selbstbewertung. </p> - <p>Die Erklärung wurde zuletzt am 15.07.2024 aktualisiert.</p> + <h2>Feedback und Kontaktangaben</h2> <p> Sie möchten uns bestehende Barrieren mitteilen oder Informationen zur Umsetzung der Barrierefreiheit erfragen? Für Ihr @@ -90,11 +111,22 @@ import { Component } from '@angular/core'; finden Sie alle Informationen zum Beschwerdeverfahren. Dort können Sie nachlesen, wie ein Beschwerdeverfahren abläuft. </p> <p>Sie erreichen die Beschwerdestelle unter folgender Adresse:</p> - <p>Beschwerdestelle nach dem Behindertengleichstellungsgesetz bei der Landesbeauftragten für Menschen mit Behinderung</p> - <p>Büroanschrift: Karolinenweg 1, 24105 Kiel</p> - <p>Postanschrift: Postfach 7121, 24171 Kiel</p> - <p>Telefon: +49 431 988 1620</p> - <p>E-Mail: <a href="mailto:bbit@landtag.ltsh.de">bbit@landtag.ltsh.de</a></p> + <h3> + Beschwerdestelle nach dem Behindertengleichstellungsgesetz bei der oder dem Landesbeauftragten für Menschen mit + Behinderungen + </h3> + <p> + <strong>Büroanschrift:</strong><br /> + Karolinenweg 1<br /> + 24105 Kiel + </p> + <p> + <strong>Postanschrift:</strong><br /> + Postfach 7121<br /> + 24171 Kiel<br /> + Telefon: +49 431 988 1620 + </p> + <p>E-Mail: <a href="mailto:bbit@landtag.ltsh.de">bbit@landtag.ltsh.de</a></p> </div>`, }) export class AccessibilityPageComponent {} diff --git a/alfa-client/apps/info/src/pages/impressum/impressum-page-sh.component.spec.ts b/alfa-client/apps/info/src/pages/impressum/impressum-page-sh.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..04481d7d8817dd7bf46eca7979be1d782e802891 --- /dev/null +++ b/alfa-client/apps/info/src/pages/impressum/impressum-page-sh.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ImpressumPageComponent } from './impressum-page-sh.component'; + +describe('ImpressumPageComponent', () => { + let component: ImpressumPageComponent; + let fixture: ComponentFixture<ImpressumPageComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ImpressumPageComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ImpressumPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/apps/info/src/pages/impressum/impressum-page-sh.component.ts b/alfa-client/apps/info/src/pages/impressum/impressum-page-sh.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..443387d3a3af5dba195f4eb173d3a965f8803605 --- /dev/null +++ b/alfa-client/apps/info/src/pages/impressum/impressum-page-sh.component.ts @@ -0,0 +1,32 @@ +import { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-impressum-page', + standalone: true, + imports: [CommonModule], + template: `<div class="ozg-prose prose prose-h1:text-4xl prose-h2:text-2xl"> + <h1>Impressum</h1> + <p> + Der Ministerpräsident - Staatskanzlei <br /> + StK 3 – Digitalisierung und Zentrales IT-Management <br /> + Düsternbrooker Weg 104 <br /> + 24105 Kiel + </p> + <p> + E-Mail: <br /> + <a href="mailto:digitalisierung@stk.landsh.de">digitalisierung@stk.landsh.de</a> + </p> + <p> + Verantwortlich <br /> + Dr. Moritz Karg <br /> + StK 30 – Grundsatzangelegenheiten der Digitalisierung und des EGovernments + </p> + + <p> + E-Mail: <br /> + <a href="mailto:digitalisierung@stk.landsh.de">digitalisierung@stk.landsh.de</a> + </p> + </div>`, +}) +export class ImpressumPageComponent {} diff --git a/alfa-client/apps/info/src/pages/impressum/impressum-page.component.spec.ts b/alfa-client/apps/info/src/pages/impressum/impressum-page.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..da6855d06a29e6365e2e83e39aff6beffef02a3c --- /dev/null +++ b/alfa-client/apps/info/src/pages/impressum/impressum-page.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ImpressumPageComponent } from './impressum-page.component'; + +describe('ImpressumPageComponent', () => { + let component: ImpressumPageComponent; + let fixture: ComponentFixture<ImpressumPageComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ImpressumPageComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ImpressumPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/apps/info/src/pages/impressum/impressum-page.component.ts b/alfa-client/apps/info/src/pages/impressum/impressum-page.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..8cc9ac2f4c4c6ae084090b7b60fdcf84aee60d77 --- /dev/null +++ b/alfa-client/apps/info/src/pages/impressum/impressum-page.component.ts @@ -0,0 +1,11 @@ +import { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-impressum-page', + standalone: true, + imports: [CommonModule], + // template will be set during build time - see fileReplacements in apps/info/project.json + template: ``, +}) +export class ImpressumPageComponent {} diff --git a/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.service.spec.ts b/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.service.spec.ts index 26aba0d2639fa0d8a0f629cf31ac1cd598b1e5bb..9432f6afd471dc29274a21701da07b25311efe29 100644 --- a/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.service.spec.ts +++ b/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.service.spec.ts @@ -49,10 +49,10 @@ describe('AdminOrganisationsEinheitService', () => { }); describe('createInKeycloak', () => { - const organisationsEiheit: AdminOrganisationsEinheit = createAdminOrganisationsEinheit(); + const organisationsEinheit: AdminOrganisationsEinheit = createAdminOrganisationsEinheit(); it('should call repository create', () => { - (service as any).createInKeycloak(organisationsEiheit); - expect(repository.create).toHaveBeenCalledWith(organisationsEiheit); + (service as any).createInKeycloak(organisationsEinheit); + expect(repository.create).toHaveBeenCalledWith(organisationsEinheit); }); }); }); diff --git a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.html b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.html index c4b125479907d2a2be8b480431e01f404d9a88e7..74ddef725dfa345af827a806dd1c0baea9ced109 100644 --- a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.html +++ b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.html @@ -28,7 +28,7 @@ <h1 class="heading-1 mb-4">Organisationseinheiten</h1> <ods-button - text="Organisationseiheit hinzufügen" + text="Organisationseinheit hinzufügen" dataTestId="add-organisationseinheit-button" class="mb-4 block" (clickEmitter)="openDialog()" diff --git a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.spec.ts b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.spec.ts index a1dedc4bbd7929e29486b2571262811ae6c5ada9..e881fea429fe05ebd5904bdda6ab0560df6ea0e0 100644 --- a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.spec.ts +++ b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.spec.ts @@ -23,8 +23,8 @@ */ import { OrganisationsEinheitContainerComponent } from '@admin-client/organisations-einheit'; import { AdminOrganisationsEinheit, AdminOrganisationsEinheitService } from '@admin-client/organisations-einheit-shared'; -import { StateResource, ToEmbeddedResourcesPipe, createStateResource } from '@alfa-client/tech-shared'; -import { Mock, existsAsHtmlElement, getMockComponent, mock } from '@alfa-client/test-utils'; +import { createStateResource, StateResource, ToEmbeddedResourcesPipe } from '@alfa-client/tech-shared'; +import { existsAsHtmlElement, getMockComponent, Mock, mock } from '@alfa-client/test-utils'; import { OzgcloudDialogService, UiModule } from '@alfa-client/ui'; import { OrganisationsEinheitResource } from '@alfa-client/zustaendige-stelle-shared'; import { CommonModule } from '@angular/common'; @@ -52,15 +52,18 @@ describe('OrganisationsEinheitContainerComponent', () => { const listSelector: string = getDataTestIdOf('organisations-einheit-list'); - beforeEach(async () => { + beforeEach(() => { organisationsEinheitService = { ...mock(AdminOrganisationsEinheitService), get: jest.fn().mockReturnValue(of(organisationsEinheitListStateResource)), create: jest.fn().mockReturnValue(of(false)), + refresh: jest.fn(), }; dialogService = mock(OzgcloudDialogService); vieContainerRef = mock(ViewContainerRef as any); + }); + beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ToEmbeddedResourcesPipe], imports: [CommonModule, UiModule, OrganisationsEinheitContainerComponent, MockComponent(OrganisationsEinheitListComponent)], @@ -90,6 +93,14 @@ describe('OrganisationsEinheitContainerComponent', () => { done(); }); }); + + describe('ngOnDestroy', () => { + it('should refresh organisationseinheiten list state resource', () => { + component.ngOnDestroy(); + + expect(organisationsEinheitService.refresh).toHaveBeenCalled(); + }); + }); }); describe('openDialog', () => { diff --git a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.ts b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.ts index aba5aabb06e438493f9faa563740560a71b8d94e..5c731384dfe222155a4186cf0021c811f878550b 100644 --- a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.ts +++ b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.ts @@ -19,7 +19,7 @@ import { ZustaendigeStelleSharedModule, } from '@alfa-client/zustaendige-stelle-shared'; import { CommonModule } from '@angular/common'; -import { Component, inject, OnInit, ViewContainerRef } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit, ViewContainerRef } from '@angular/core'; import { ButtonComponent } from '@ods/system'; import { filter, first, Observable } from 'rxjs'; import { SearchZustaendigeStelleDialogComponent } from '../../../../../zustaendige-stelle/src/lib/search-zustaendige-stelle-dialog/search-zustaendige-stelle-dialog.component'; @@ -46,7 +46,7 @@ import { OrganisationsEinheitListComponent } from './organisations-einheit-list/ }, ], }) -export class OrganisationsEinheitContainerComponent implements OnInit { +export class OrganisationsEinheitContainerComponent implements OnInit, OnDestroy { private readonly organisationsEinheitService = inject(AdminOrganisationsEinheitService); private readonly dialogService = inject(OzgcloudDialogService); private readonly viewContainerRef = inject(ViewContainerRef); @@ -58,6 +58,10 @@ export class OrganisationsEinheitContainerComponent implements OnInit { this.organisationsEinheitenStateResource$ = this.organisationsEinheitService.get(); } + ngOnDestroy(): void { + this.organisationsEinheitService.refresh(); + } + openDialog(): void { this.dialogService .openFullScreenDialog<SearchZustaendigeStelleDialogComponent<OrganisationsEinheitResource>>( diff --git a/alfa-client/libs/admin/user/src/lib/users-roles/users-roles.component.spec.ts b/alfa-client/libs/admin/user/src/lib/users-roles/users-roles.component.spec.ts index 8532424baa0925e4b9fa9b71525382b98edb8e0c..feabd22992fa379e1ec37308132052edde220a2a 100644 --- a/alfa-client/libs/admin/user/src/lib/users-roles/users-roles.component.spec.ts +++ b/alfa-client/libs/admin/user/src/lib/users-roles/users-roles.component.spec.ts @@ -37,16 +37,25 @@ import { UsersRolesComponent } from './users-roles.component'; describe('UsersRolesComponent', () => { let component: UsersRolesComponent; let fixture: ComponentFixture<UsersRolesComponent>; - let router: Router; - const userService: Mock<UserService> = { - ...mock(UserService), - get: jest.fn(), - }; + let router: Mock<Router>; + let userService: Mock<UserService>; + + beforeEach(() => { + userService = { + ...mock(UserService), + get: jest.fn(), + refresh: jest.fn(), + }; + router = mock(Router); + }); beforeEach(async () => { await TestBed.configureTestingModule({ - providers: [{ provide: UserService, useValue: userService }], + providers: [ + { provide: UserService, useValue: userService }, + { provide: Router, useValue: router }, + ], imports: [ MockComponent(ButtonWithSpinnerComponent), MockComponent(MailboxIconComponent), @@ -56,9 +65,6 @@ describe('UsersRolesComponent', () => { ], }).compileComponents(); - TestBed.inject(UserService); - router = TestBed.inject(Router); - fixture = TestBed.createComponent(UsersRolesComponent); component = fixture.componentInstance; fixture.detectChanges(); @@ -76,13 +82,19 @@ describe('UsersRolesComponent', () => { }); }); + describe('ngOnDestroy', () => { + it('should refresh users state resource', () => { + component.ngOnDestroy(); + + expect(userService.refresh).toHaveBeenCalled(); + }); + }); + describe('navigateToAddUser', () => { it('should navigate to add user', () => { - const routerSpy = jest.spyOn(router as any, 'navigate'); - component.navigateToAddUser(); - expect(routerSpy).toHaveBeenCalledWith([ROUTES.BENUTZER_UND_ROLLEN_NEU]); + expect(router.navigate).toHaveBeenCalledWith([ROUTES.BENUTZER_UND_ROLLEN_NEU]); }); }); diff --git a/alfa-client/libs/admin/user/src/lib/users-roles/users-roles.component.ts b/alfa-client/libs/admin/user/src/lib/users-roles/users-roles.component.ts index 027e57aec2f26b7d10a359a306678c6fc36000c6..916c81eaf876f94c6d5b18f8fe6c7803f55d2b00 100644 --- a/alfa-client/libs/admin/user/src/lib/users-roles/users-roles.component.ts +++ b/alfa-client/libs/admin/user/src/lib/users-roles/users-roles.component.ts @@ -25,7 +25,7 @@ import { ROUTES } from '@admin-client/shared'; import { ToUserNamePipe, User, UserService } from '@admin-client/user-shared'; import { StateResource } from '@alfa-client/tech-shared'; import { CommonModule } from '@angular/common'; -import { Component, inject, OnInit } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { ButtonWithSpinnerComponent } from '@ods/component'; import { ListComponent, ListItemComponent, MailboxIconComponent, PersonIconComponent } from '@ods/system'; @@ -45,17 +45,21 @@ import { Observable } from 'rxjs'; ToUserNamePipe, ], }) -export class UsersRolesComponent implements OnInit { +export class UsersRolesComponent implements OnInit, OnDestroy { private router = inject(Router); private userService = inject(UserService); public users$: Observable<StateResource<User[]>>; public readonly GROUPS_TO_DISPLAY: number = 3; - ngOnInit() { + ngOnInit(): void { this.users$ = this.userService.get(); } + ngOnDestroy(): void { + this.userService.refresh(); + } + public navigateToAddUser(): void { this.router.navigate([ROUTES.BENUTZER_UND_ROLLEN_NEU]); }