diff --git a/alfa-client/.prettierignore b/alfa-client/.prettierignore index ee7824bd9c6d18d685c77479a451c9554c111e9d..0eb1899d00dd4018fa9605a6b746a08a07174333 100644 --- a/alfa-client/.prettierignore +++ b/alfa-client/.prettierignore @@ -6,4 +6,5 @@ packages/workspace/src/generators/**/files/**/*.json /.nx/cache .angular -/.nx/workspace-data \ No newline at end of file +/.nx/workspace-data +*.md \ No newline at end of file diff --git a/alfa-client/apps/admin/src/main.ts b/alfa-client/apps/admin/src/main.ts index 7f198e2f5459037b727eb464f2259321948eeaed..de7bed8ea66ac6e222311d9d8c185b4cad477b85 100644 --- a/alfa-client/apps/admin/src/main.ts +++ b/alfa-client/apps/admin/src/main.ts @@ -21,15 +21,14 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { EnvironmentModule, loadEnvironment } from '@alfa-client/environment-shared'; -import { enableProdMode, importProvidersFrom, Injectable } from '@angular/core'; -import { isNil } from 'lodash-es'; - +import { AggregationMappingProvider } from '@admin-client/reporting-shared'; import { ApiRootModule } from '@alfa-client/api-root-shared'; +import { EnvironmentModule, loadEnvironment } from '@alfa-client/environment-shared'; 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 { enableProdMode, importProvidersFrom, Injectable } from '@angular/core'; import { bootstrapApplication, BrowserModule } from '@angular/platform-browser'; import { provideAnimations } from '@angular/platform-browser/animations'; import { @@ -49,6 +48,7 @@ import { ConfigurationsProviders } from 'libs/admin/configuration-shared/src/lib import { PostfachProviders } from 'libs/admin/postfach-shared/src/lib/postfach.providers'; import { SettingsProviders } from 'libs/admin/settings-shared/src/lib/settings.providers'; import { UserProviders } from 'libs/admin/user/src/lib/user.providers'; +import { isNil } from 'lodash-es'; import { HttpBinaryFileInterceptor } from '../../../libs/tech-shared/src/lib/interceptor/http-binary-file.interceptor'; import { HttpXsrfInterceptor } from '../../../libs/tech-shared/src/lib/interceptor/http-xsrf.interceptor'; import { XhrInterceptor } from '../../../libs/tech-shared/src/lib/interceptor/xhr.interceptor'; @@ -97,6 +97,7 @@ loadEnvironment(environment.environmentUrl).then((env) => { PostfachProviders, SettingsProviders, UserProviders, + AggregationMappingProvider, importProvidersFrom( NavigationSharedModule, BrowserModule, diff --git a/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.spec.ts b/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.spec.ts index a861afd0728827a45330d8d5d2d78f63e6dfc5d1..938cc1b0061d2194105629ca70c1e5f8619d881c 100644 --- a/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.spec.ts +++ b/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.spec.ts @@ -1,7 +1,7 @@ +import { AdminStatistikFieldsFormComponent } from '@admin-client/statistik'; import { existsAsHtmlElement } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MockComponent } from 'ng-mocks'; -import { AdminStatistikFieldsFormComponent } from '../../../../../../libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component'; import { getDataTestIdOf } from '../../../../../../libs/tech-shared/test/data-test'; import { StatistikFieldsFormPageComponent } from './statistik-fields-form-page.component'; diff --git a/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.ts b/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.ts index e249c034417fc7133d8cc2c5db690ddcb94ea50f..c4122b72d148865b4219d5446a3c2060574b23e7 100644 --- a/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.ts +++ b/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.ts @@ -1,6 +1,6 @@ +import { AdminStatistikFieldsFormComponent } from '@admin-client/statistik'; import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; -import { AdminStatistikFieldsFormComponent } from '../../../../../../libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component'; @Component({ selector: 'statistik-fields-form-page', diff --git a/alfa-client/libs/admin/postfach-shared/project.json b/alfa-client/libs/admin/postfach-shared/project.json index 6489dc23d02f10a0660d03ccd39ccebae8dadc5d..e02554623fc02a4f2929efea43c8669d24ecdf3a 100644 --- a/alfa-client/libs/admin/postfach-shared/project.json +++ b/alfa-client/libs/admin/postfach-shared/project.json @@ -10,7 +10,7 @@ "executor": "@nx/jest:jest", "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], "options": { - "tsConfig": "libs/admin//postfach-shared/tsconfig.spec.json", + "tsConfig": "libs/admin/postfach-shared/tsconfig.spec.json", "jestConfig": "libs/admin/postfach-shared/jest.config.ts" } }, diff --git a/alfa-client/libs/admin/postfach-shared/src/lib/postfach-resource.service.ts b/alfa-client/libs/admin/postfach-shared/src/lib/postfach-resource.service.ts index 48043163dea114eba85fa881d9047ec90f8ef24a..a617ed3abf3f31d102889877f0d10a8117bb23b2 100644 --- a/alfa-client/libs/admin/postfach-shared/src/lib/postfach-resource.service.ts +++ b/alfa-client/libs/admin/postfach-shared/src/lib/postfach-resource.service.ts @@ -21,7 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { SettingsService } from '@admin-client/settings-shared'; +import { SettingName, SettingsService } from '@admin-client/settings-shared'; import { ApiResourceService, ResourceRepository, ResourceServiceConfig } from '@alfa-client/tech-shared'; import { PostfachLinkRel } from './postfach.linkrel'; import { PostfachResource } from './postfach.model'; @@ -34,7 +34,7 @@ export function createPostfachResourceService(repository: ResourceRepository, se function buildConfig(settingService: SettingsService): ResourceServiceConfig<PostfachResource> { return { - resource: settingService.getPostfach(), + resource: settingService.get(SettingName.POSTFACH), getLinkRel: PostfachLinkRel.SELF, edit: { linkRel: PostfachLinkRel.SELF }, }; diff --git a/alfa-client/libs/admin/postfach-shared/src/lib/postfach.model.ts b/alfa-client/libs/admin/postfach-shared/src/lib/postfach.model.ts index c592dec3e6621543ca69e9ee23abdeea1ec6f4ec..e3ce2fb8bd6153796bea9bf82422718d2324721e 100644 --- a/alfa-client/libs/admin/postfach-shared/src/lib/postfach.model.ts +++ b/alfa-client/libs/admin/postfach-shared/src/lib/postfach.model.ts @@ -21,8 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Resource } from '@ngxp/rest'; -import { SettingName } from 'libs/admin/settings-shared/src/lib/settings.model'; +import { SettingItemResource } from 'libs/admin/settings-shared/src/lib/settings.model'; export interface Absender { name: string; @@ -37,9 +36,4 @@ export interface Postfach { signatur: string; } -export declare type PostfachSettingsItem = { - name: SettingName.POSTFACH; - settingBody: Postfach; -}; - -export declare type PostfachResource = Resource & PostfachSettingsItem; +export declare type PostfachResource = SettingItemResource<Postfach>; diff --git a/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.spec.ts b/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.spec.ts index d779cb50dc2173e7dee4dc929a7e11f5d259724d..2441dc06db2b787fe3f33389d1fb2daae412e3bf 100644 --- a/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.spec.ts +++ b/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.spec.ts @@ -21,6 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { SettingItem } from '@admin-client/settings-shared'; import { createEmptyStateResource, createStateResource, StateResource } from '@alfa-client/tech-shared'; import { Mock, mock } from '@alfa-client/test-utils'; import { SnackBarService } from '@alfa-client/ui'; @@ -31,7 +32,7 @@ import { singleCold, singleHot } from 'libs/tech-shared/test/marbles'; import { Observable, of } from 'rxjs'; import { createPostfachResource, createPostfachSettingItem } from '../test/postfach'; import { PostfachResourceService } from './postfach-resource.service'; -import { PostfachResource, PostfachSettingsItem } from './postfach.model'; +import { Postfach, PostfachResource } from './postfach.model'; import { PostfachService } from './postfach.service'; describe('PostfachService', () => { @@ -78,7 +79,7 @@ describe('PostfachService', () => { }); describe('save', () => { - const postfachSettingsItem: PostfachSettingsItem = createPostfachSettingItem(); + const postfachSettingsItem: SettingItem<Postfach> = createPostfachSettingItem(); const postfachResource: PostfachResource = createPostfachResource(); const postfachStateResource: StateResource<PostfachResource> = createStateResource(postfachResource); diff --git a/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.ts b/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.ts index 19cf4ef6cf561586b44f3afee70e4c716ec54052..3321cfc1690bd504a1e0f94ac6eda627170a29c9 100644 --- a/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.ts +++ b/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.ts @@ -21,13 +21,13 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { SettingName } from '@admin-client/settings-shared'; +import { SettingItem, SettingName } from '@admin-client/settings-shared'; import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; import { SnackBarService } from '@alfa-client/ui'; import { inject, Injectable } from '@angular/core'; import { Observable, startWith, tap } from 'rxjs'; import { PostfachResourceService } from './postfach-resource.service'; -import { Postfach, PostfachResource, PostfachSettingsItem } from './postfach.model'; +import { Postfach, PostfachResource } from './postfach.model'; @Injectable() export class PostfachService { @@ -51,7 +51,7 @@ export class PostfachService { } } - private buildPostfachSettingItem(postfach: Postfach): PostfachSettingsItem { + private buildPostfachSettingItem(postfach: Postfach): SettingItem<Postfach> { return { name: SettingName.POSTFACH, settingBody: postfach, diff --git a/alfa-client/libs/admin/postfach-shared/src/test/postfach.ts b/alfa-client/libs/admin/postfach-shared/src/test/postfach.ts index 93d7492c32b0e0b595a447757952ee358b0e1345..88132bc3d88c73d130f50caed0da9aa15ae68d5b 100644 --- a/alfa-client/libs/admin/postfach-shared/src/test/postfach.ts +++ b/alfa-client/libs/admin/postfach-shared/src/test/postfach.ts @@ -22,9 +22,9 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { faker } from '@faker-js/faker'; -import { SettingItemResource, SettingName } from 'libs/admin/settings-shared/src/lib/settings.model'; +import { SettingItem, SettingName } from 'libs/admin/settings-shared/src/lib/settings.model'; import { toResource } from 'libs/tech-shared/test/resource'; -import { Postfach, PostfachResource, PostfachSettingsItem } from '../lib/postfach.model'; +import { Postfach, PostfachResource } from '../lib/postfach.model'; export function createPostfach(): Postfach { return { @@ -39,7 +39,7 @@ export function createPostfach(): Postfach { }; } -export function createPostfachSettingItem(): PostfachSettingsItem { +export function createPostfachSettingItem(): SettingItem<Postfach> { return { name: SettingName.POSTFACH, settingBody: createPostfach(), @@ -49,10 +49,3 @@ export function createPostfachSettingItem(): PostfachSettingsItem { export function createPostfachResource(): PostfachResource { return toResource(createPostfachSettingItem()); } - -export function createSettingItemResource(): SettingItemResource { - return toResource({ - name: faker.word.sample(), - settingBody: {}, - }); -} diff --git a/alfa-client/libs/admin/reporting-shared/.eslintrc.json b/alfa-client/libs/admin/reporting-shared/.eslintrc.json new file mode 100644 index 0000000000000000000000000000000000000000..7474579d583c598ae092a906b3e6cf1ad3c08a50 --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": ["plugin:@nx/angular", "plugin:@angular-eslint/template/process-inline-templates"], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "admin", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "admin", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/alfa-client/libs/admin/reporting-shared/README.md b/alfa-client/libs/admin/reporting-shared/README.md new file mode 100644 index 0000000000000000000000000000000000000000..be5bb22469ec116a5b8b3d498db669555c92da4e --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/README.md @@ -0,0 +1,7 @@ +# admin-reporting-shared + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test admin-reporting-shared` to execute the unit tests. diff --git a/alfa-client/libs/admin/reporting-shared/jest.config.ts b/alfa-client/libs/admin/reporting-shared/jest.config.ts new file mode 100644 index 0000000000000000000000000000000000000000..b4f392a061baff74cb98d86a02f0100960384315 --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/jest.config.ts @@ -0,0 +1,21 @@ +export default { + displayName: 'admin-reporting-shared', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], + coverageDirectory: '../../../coverage/libs/admin/reporting-shared', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '<rootDir>/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/alfa-client/libs/admin/reporting-shared/project.json b/alfa-client/libs/admin/reporting-shared/project.json new file mode 100644 index 0000000000000000000000000000000000000000..f7a760900530aff273dc117494292f1e2b028824 --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/project.json @@ -0,0 +1,22 @@ +{ + "name": "admin-reporting-shared", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/admin/reporting-shared/src", + "prefix": "admin", + "projectType": "library", + "tags": [], + "targets": { + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "tsConfig": "libs/admin/reporting-shared/tsconfig.spec.json", + "jestConfig": "libs/admin/reporting-shared/jest.config.ts" + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + } + } +} diff --git a/alfa-client/libs/admin/reporting-shared/src/index.ts b/alfa-client/libs/admin/reporting-shared/src/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..44ab78233d97deaf497e327e28f71c431ae6960e --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/index.ts @@ -0,0 +1,4 @@ +export * from './lib/aggregation-mapping-resource.service'; +export * from './lib/aggregation-mapping.model'; +export * from './lib/aggregation-mapping.provider'; +export * from './lib/aggregation-mapping.service'; diff --git a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-resource.service.ts b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-resource.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..65eb7a8e1680b97f12cdba2556ae8d9d9041883c --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-resource.service.ts @@ -0,0 +1,26 @@ +import { ConfigurationLinkRel, ConfigurationResource, ConfigurationService } from '@admin-client/configuration-shared'; +import { ListResourceServiceConfig, ResourceListService, ResourceRepository } from '@alfa-client/tech-shared'; +import { AggregationMappingListLinkRel } from './aggregation-mapping.linkrel'; +import { AggregationMappingListResource, AggregationMappingResource } from './aggregation-mapping.model'; + +export class AggregationMappingListResourceService extends ResourceListService< + ConfigurationResource, + AggregationMappingListResource, + AggregationMappingResource +> {} + +export function createAggregationMappingResourceService( + repository: ResourceRepository, + configurationService: ConfigurationService, +) { + return new ResourceListService(buildConfig(configurationService), repository); +} + +function buildConfig(configurationService: ConfigurationService): ListResourceServiceConfig<ConfigurationResource> { + return { + baseResource: configurationService.get(), + listLinkRel: ConfigurationLinkRel.AGGREGATION_MAPPINGS, + listResourceListLinkRel: AggregationMappingListLinkRel.LIST, + createLinkRel: AggregationMappingListLinkRel.SELF, + }; +} diff --git a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.linkrel.ts b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.linkrel.ts new file mode 100644 index 0000000000000000000000000000000000000000..a4a02bfd7087b105a9d0d2dd816aa2b180a4db9c --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.linkrel.ts @@ -0,0 +1,4 @@ +export enum AggregationMappingListLinkRel { + LIST = 'aggregationMappings', + SELF = 'self', +} diff --git a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.model.ts b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..0db6cb4e397768b10781b3c34c9379fc866cec2b --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.model.ts @@ -0,0 +1,20 @@ +import { ListResource } from '@alfa-client/tech-shared'; +import { Resource } from '@ngxp/rest'; + +export interface AggregationMapping { + formIdentifier: FormIdentifier; + mappings: FieldMapping[]; +} + +export interface FormIdentifier { + formEngineName: string; + formId: string; +} + +export interface FieldMapping { + sourcePath: string; + targetPath: string; +} + +export interface AggregationMappingResource extends AggregationMapping, Resource {} +export interface AggregationMappingListResource extends ListResource {} diff --git a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.provider.ts b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.provider.ts new file mode 100644 index 0000000000000000000000000000000000000000..888d7154e81dbfe53fb7476e4de8835463de6d25 --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.provider.ts @@ -0,0 +1,17 @@ +import { ResourceRepository } from '@alfa-client/tech-shared'; +import { Provider } from '@angular/core'; +import { ConfigurationService } from 'libs/admin/configuration-shared/src/lib/configuration.service'; +import { + AggregationMappingListResourceService, + createAggregationMappingResourceService, +} from './aggregation-mapping-resource.service'; +import { AggregationMappingService } from './aggregation-mapping.service'; + +export const AggregationMappingProvider: Provider[] = [ + { + provide: AggregationMappingListResourceService, + useFactory: createAggregationMappingResourceService, + deps: [ResourceRepository, ConfigurationService], + }, + AggregationMappingService, +]; diff --git a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.service.spec.ts b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..ecf4387ceee6a3dbdd6775f8ef4fa3a306144002 --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.service.spec.ts @@ -0,0 +1,79 @@ +import { StateResource, createStateResource } from '@alfa-client/tech-shared'; +import { Mock, mock } from '@alfa-client/test-utils'; +import { TestBed } from '@angular/core/testing'; +import { singleCold } from 'libs/tech-shared/test/marbles'; +import { Observable } from 'rxjs'; +import { + createAggregationMapping, + createAggregationMappingListResource, + createAggregationMappingResource, +} from '../../test/aggregation-mapping'; +import { AggregationMappingListResourceService } from './aggregation-mapping-resource.service'; +import { AggregationMapping, AggregationMappingListResource, AggregationMappingResource } from './aggregation-mapping.model'; +import { AggregationMappingService } from './aggregation-mapping.service'; + +describe('AggregationMappingService', () => { + let service: AggregationMappingService; + let listResourceService: Mock<AggregationMappingListResourceService>; + + beforeEach(() => { + listResourceService = mock(AggregationMappingListResourceService); + + TestBed.configureTestingModule({ + providers: [AggregationMappingService, { provide: AggregationMappingListResourceService, useValue: listResourceService }], + }); + + service = TestBed.inject(AggregationMappingService); + }); + + it('should create', () => { + expect(service).toBeTruthy(); + }); + + describe('get list', () => { + const aggregationMappingListResource: AggregationMappingListResource = createAggregationMappingListResource(); + const aggregationMappingListStateResource: StateResource<AggregationMappingListResource> = + createStateResource(aggregationMappingListResource); + + beforeEach(() => { + listResourceService.getList = jest.fn().mockReturnValue(singleCold(aggregationMappingListStateResource)); + }); + + it('should call listResourceService', () => { + service.getList(); + + expect(listResourceService.getList).toHaveBeenCalled(); + }); + + it('should return value', () => { + const loadedAggregationMappingListResource: Observable<StateResource<AggregationMappingListResource>> = service.getList(); + + expect(loadedAggregationMappingListResource).toBeObservable(singleCold(aggregationMappingListStateResource)); + }); + }); + + describe('create', () => { + const aggregationMappingResource: AggregationMappingResource = createAggregationMappingResource(); + const aggregationMappingStateResource: StateResource<AggregationMappingResource> = + createStateResource(aggregationMappingResource); + + const aggregationMapping: AggregationMapping = createAggregationMapping(); + + beforeEach(() => { + listResourceService.create = jest.fn().mockReturnValue(singleCold(aggregationMappingStateResource)); + }); + + it('should call resourceService', () => { + service.create(aggregationMapping); + + expect(listResourceService.create).toHaveBeenCalledWith(aggregationMapping); + }); + + it('should return value', () => { + const loadedAggregationMappingResource: Observable<StateResource<AggregationMappingResource>> = + service.create(aggregationMapping); + + expect(loadedAggregationMappingResource).toBeObservable(singleCold(aggregationMappingStateResource)); + }); + }); +}); diff --git a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.service.ts b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..20a8ce6c1ccda133323bac99a671d887e77afb8e --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.service.ts @@ -0,0 +1,18 @@ +import { StateResource } from '@alfa-client/tech-shared'; +import { Injectable, inject } from '@angular/core'; +import { Observable } from 'rxjs'; +import { AggregationMappingListResourceService } from './aggregation-mapping-resource.service'; +import { AggregationMapping, AggregationMappingListResource, AggregationMappingResource } from './aggregation-mapping.model'; + +@Injectable() +export class AggregationMappingService { + readonly listService = inject(AggregationMappingListResourceService); + + public getList(): Observable<StateResource<AggregationMappingListResource>> { + return this.listService.getList(); + } + + public create(toCreate: AggregationMapping): Observable<StateResource<AggregationMappingResource>> { + return this.listService.create(toCreate); + } +} diff --git a/alfa-client/libs/admin/reporting-shared/src/test-setup.ts b/alfa-client/libs/admin/reporting-shared/src/test-setup.ts new file mode 100644 index 0000000000000000000000000000000000000000..c408668266d2fec3a9803c0ec044bc163fb987fe --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/test-setup.ts @@ -0,0 +1,12 @@ +import '@testing-library/jest-dom'; +import 'jest-preset-angular/setup-jest'; + +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +getTestBed().resetTestEnvironment(); +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { + teardown: { destroyAfterEach: false }, + errorOnUnknownProperties: true, + errorOnUnknownElements: true, +}); diff --git a/alfa-client/libs/admin/reporting-shared/test/aggregation-mapping.ts b/alfa-client/libs/admin/reporting-shared/test/aggregation-mapping.ts new file mode 100644 index 0000000000000000000000000000000000000000..acbcc9fa75722396e4a410ea77e9157a2a8ce8fc --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/test/aggregation-mapping.ts @@ -0,0 +1,35 @@ +import { faker } from '@faker-js/faker'; +import { times } from 'lodash-es'; +import { LinkRelationName } from '../../../tech-shared/src'; +import { toResource } from '../../../tech-shared/test/resource'; +import { AggregationMapping, AggregationMappingListResource, AggregationMappingResource } from '../src'; +import { AggregationMappingListLinkRel } from '../src/lib/aggregation-mapping.linkrel'; + +export function createAggregationMapping(): AggregationMapping { + return { + formIdentifier: { + formEngineName: faker.lorem.word(), + formId: faker.string.uuid(), + }, + mappings: [ + { + sourcePath: faker.lorem.word(), + targetPath: faker.lorem.word(), + }, + ], + }; +} + +export function createAggregationMappingResource(linkRelations: LinkRelationName[] = []): AggregationMappingResource { + return toResource(createAggregationMapping(), linkRelations); +} + +export function createAggregationMappingResources(linkRelations: LinkRelationName[] = []): AggregationMappingResource[] { + return times(10, () => createAggregationMappingResource(linkRelations)); +} + +export function createAggregationMappingListResource(linkRelations: LinkRelationName[] = []): AggregationMappingListResource { + return toResource({}, linkRelations, { + [AggregationMappingListLinkRel.LIST]: createAggregationMappingResources(), + }); +} diff --git a/alfa-client/libs/admin/reporting-shared/tsconfig.json b/alfa-client/libs/admin/reporting-shared/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..8ca9ad312c2bd4dc364383853ddd91a2ed8f86fd --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "target": "es2022" + } +} diff --git a/alfa-client/libs/admin/reporting-shared/tsconfig.lib.json b/alfa-client/libs/admin/reporting-shared/tsconfig.lib.json new file mode 100644 index 0000000000000000000000000000000000000000..8441346f6e5858b2ef4235cb3c3160eda256f94a --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/tsconfig.lib.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": ["src/**/*.spec.ts", "src/test-setup.ts", "jest.config.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/alfa-client/libs/admin/reporting-shared/tsconfig.spec.json b/alfa-client/libs/admin/reporting-shared/tsconfig.spec.json new file mode 100644 index 0000000000000000000000000000000000000000..723782fbd367969806c5992aea882773ab65af8b --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/tsconfig.spec.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"], + "target": "ES2022", + "useDefineForClassFields": false + }, + "files": ["src/test-setup.ts"], + "include": ["**/*.test.ts", "**/*.spec.ts", "**/*.d.ts", "jest.config.ts"] +} diff --git a/alfa-client/libs/admin/settings-shared/jest.config.ts b/alfa-client/libs/admin/settings-shared/jest.config.ts index e9e4c0e81848959b2203b3551b8a0e999c127500..9c940034939a689a0f138e47d5c3a5c34d2bb92e 100644 --- a/alfa-client/libs/admin/settings-shared/jest.config.ts +++ b/alfa-client/libs/admin/settings-shared/jest.config.ts @@ -1,9 +1,8 @@ export default { - displayName: 'admin-settings', + displayName: 'admin-settings-shared', preset: '../../../jest.preset.js', setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], - globals: {}, - coverageDirectory: '../../coverage/libs/admin/settings-shared', + coverageDirectory: '../../../coverage/libs/admin/settings-shared', transform: { '^.+\\.(ts|mjs|js|html)$': [ 'jest-preset-angular', @@ -13,7 +12,7 @@ export default { }, ], }, - transformIgnorePatterns: ['node_modules/(?!.pnpm|.*\\.mjs$)'], + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], snapshotSerializers: [ 'jest-preset-angular/build/serializers/no-ng-attributes', 'jest-preset-angular/build/serializers/ng-snapshot', diff --git a/alfa-client/libs/admin/settings-shared/project.json b/alfa-client/libs/admin/settings-shared/project.json index 702a27446f83db42dc97ec9409c8f3d323dcbb65..02e461c6816868190ac3b45dea543a934a1d6d1d 100644 --- a/alfa-client/libs/admin/settings-shared/project.json +++ b/alfa-client/libs/admin/settings-shared/project.json @@ -1,15 +1,11 @@ { - "name": "admin-settings", + "name": "admin-settings-shared", "$schema": "../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "libs/admin/settings-shared/src", - "projectType": "library", "prefix": "admin", + "projectType": "library", "tags": [], "targets": { - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] - }, "test": { "executor": "@nx/jest:jest", "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], @@ -17,6 +13,10 @@ "tsConfig": "libs/admin/settings-shared/tsconfig.spec.json", "jestConfig": "libs/admin/settings-shared/jest.config.ts" } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] } } } diff --git a/alfa-client/libs/admin/settings-shared/src/lib/settings-resource.service.ts b/alfa-client/libs/admin/settings-shared/src/lib/settings-resource.service.ts index a75f91afec901a8c969c00ede9a8631766421f1f..7e9df14e6ec13929f720978ada7ebcb3e74c5ef9 100644 --- a/alfa-client/libs/admin/settings-shared/src/lib/settings-resource.service.ts +++ b/alfa-client/libs/admin/settings-shared/src/lib/settings-resource.service.ts @@ -4,7 +4,11 @@ import { Resource } from '@ngxp/rest'; import { SettingListLinkRel } from './settings.linkrel'; import { SettingItemResource, SettingListResource } from './settings.model'; -export class SettingListResourceService extends ResourceListService<Resource, SettingListResource, SettingItemResource> {} +export class SettingListResourceService extends ResourceListService< + Resource, + SettingListResource, + SettingItemResource<unknown> +> {} export function createSettingListResourceService(repository: ResourceRepository, configurationService: ConfigurationService) { return new ResourceListService(buildConfig(configurationService), repository); diff --git a/alfa-client/libs/admin/settings-shared/src/lib/settings.model.ts b/alfa-client/libs/admin/settings-shared/src/lib/settings.model.ts index 263fca126d53eb889e1701e931a8b32a38899145..2e97584b7934ea105c828cbb2523c0d16ce431a1 100644 --- a/alfa-client/libs/admin/settings-shared/src/lib/settings.model.ts +++ b/alfa-client/libs/admin/settings-shared/src/lib/settings.model.ts @@ -25,16 +25,16 @@ import { ListResource } from '@alfa-client/tech-shared'; import { Resource } from '@ngxp/rest'; export interface SettingListResource extends ListResource { - _embedded: { settings: SettingItemResource[] }; + _embedded: { settings: SettingItemResource<unknown>[] }; } export enum SettingName { POSTFACH = 'Postfach', } -export interface SettingItem { +export interface SettingItem<T> { name: SettingName; - settingBody: unknown; + settingBody: T; } -export declare type SettingItemResource = Resource & SettingItem; +export interface SettingItemResource<T> extends SettingItem<T>, Resource {} diff --git a/alfa-client/libs/admin/settings-shared/src/lib/settings.service.spec.ts b/alfa-client/libs/admin/settings-shared/src/lib/settings.service.spec.ts index 9b24d56767a9f5de93f738638d976333cfa93af7..1cf23c357186c0480ce12a349497c2c1f66139e5 100644 --- a/alfa-client/libs/admin/settings-shared/src/lib/settings.service.spec.ts +++ b/alfa-client/libs/admin/settings-shared/src/lib/settings.service.spec.ts @@ -21,16 +21,14 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { PostfachResource } from '@admin-client/postfach-shared'; import { StateResource, createEmptyStateResource, createStateResource } from '@alfa-client/tech-shared'; import { Mock, mock } from '@alfa-client/test-utils'; import { TestBed } from '@angular/core/testing'; import { singleCold } from 'libs/tech-shared/test/marbles'; import { Observable, of } from 'rxjs'; -import { createPostfachResource, createSettingItemResource } from '../../../postfach-shared/src/test/postfach'; -import { createSettingsListResource } from '../../test/settings'; +import { createSettingItemResource, createSettingsListResource } from '../../test/settings'; import { SettingListResourceService } from './settings-resource.service'; -import { SettingListResource } from './settings.model'; +import { SettingItemResource, SettingListResource, SettingName } from './settings.model'; import { SettingsService } from './settings.service'; describe('SettingsService', () => { @@ -51,11 +49,12 @@ describe('SettingsService', () => { expect(service).toBeTruthy(); }); - describe('get Postfach', () => { - const postfachResource = createPostfachResource(); - const postfachStateResource: StateResource<PostfachResource> = createStateResource(postfachResource); + describe('get by setting name', () => { + const settingItemName: SettingName = <SettingName>'DUMMY'; + const settingItemResource: SettingItemResource<unknown> = { ...createSettingItemResource(), name: settingItemName }; + const settingItemStateResource: StateResource<SettingItemResource<unknown>> = createStateResource(settingItemResource); const settingsListResource: StateResource<SettingListResource> = createStateResource( - createSettingsListResource([postfachResource]), + createSettingsListResource([settingItemResource]), ); beforeEach(() => { @@ -63,28 +62,28 @@ describe('SettingsService', () => { }); it('should call resource service', () => { - service.getPostfach(); + service.get(settingItemName); expect(settingListResourceService.getList).toHaveBeenCalled(); }); - it('should return null for non postfach resource', () => { + it('should return null for non existing setting item resource', () => { const emptySettingsListResource: StateResource<SettingListResource> = createStateResource( createSettingsListResource([createSettingItemResource()]), ); - settingListResourceService.getList = jest.fn().mockReturnValue(singleCold(emptySettingsListResource)); - const postfach: Observable<StateResource<PostfachResource>> = service.getPostfach(); - expect(postfach).toBeObservable(singleCold(createEmptyStateResource())); + const settingItemStateResource$: Observable<StateResource<SettingItemResource<unknown>>> = service.get(settingItemName); + + expect(settingItemStateResource$).toBeObservable(singleCold(createEmptyStateResource())); }); - it('should return item resource as postfach resource', () => { + it('should return item resource as setting item resource', () => { settingListResourceService.getList = jest.fn().mockReturnValue(singleCold(settingsListResource)); - const postfach: Observable<StateResource<PostfachResource>> = service.getPostfach(); + const settingItemStateResource$: Observable<StateResource<SettingItemResource<unknown>>> = service.get(settingItemName); - expect(postfach).toBeObservable(singleCold(postfachStateResource)); + expect(settingItemStateResource$).toBeObservable(singleCold(settingItemStateResource)); }); }); }); diff --git a/alfa-client/libs/admin/settings-shared/src/lib/settings.service.ts b/alfa-client/libs/admin/settings-shared/src/lib/settings.service.ts index 51faa2be7ea9aa2a9f878de233ad56d32ec0f81c..770c9d7da6230f67e7d92c553e97e062277b5994 100644 --- a/alfa-client/libs/admin/settings-shared/src/lib/settings.service.ts +++ b/alfa-client/libs/admin/settings-shared/src/lib/settings.service.ts @@ -1,15 +1,17 @@ -import { PostfachResource } from '@admin-client/postfach-shared'; import { StateResource } from '@alfa-client/tech-shared'; import { inject, Injectable } from '@angular/core'; import { map, Observable } from 'rxjs'; import { SettingListResourceService } from './settings-resource.service'; -import { getPostfachResource } from './settings.util'; +import { SettingItemResource, SettingListResource, SettingName } from './settings.model'; +import { getSettingItemResource } from './settings.util'; @Injectable() export class SettingsService { private settingListResourceService = inject(SettingListResourceService); - public getPostfach(): Observable<StateResource<PostfachResource>> { - return this.settingListResourceService.getList().pipe(map(getPostfachResource)); + public get<T>(name: SettingName): Observable<StateResource<SettingItemResource<T>>> { + return this.settingListResourceService + .getList() + .pipe(map((list: StateResource<SettingListResource>) => getSettingItemResource<T>(list, name))); } } diff --git a/alfa-client/libs/admin/settings-shared/src/lib/settings.util.spec.ts b/alfa-client/libs/admin/settings-shared/src/lib/settings.util.spec.ts index 5b78df34aa506c8180878416054cd11bb4164d14..fe8480f147ba265070096e70d6fe54f817324397 100644 --- a/alfa-client/libs/admin/settings-shared/src/lib/settings.util.spec.ts +++ b/alfa-client/libs/admin/settings-shared/src/lib/settings.util.spec.ts @@ -21,30 +21,31 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { PostfachResource } from '@admin-client/postfach-shared'; -import { SettingListResource } from '@admin-client/settings-shared'; import { createEmptyStateResource, createStateResource, StateResource } from '@alfa-client/tech-shared'; -import { createPostfachResource } from 'libs/admin/postfach-shared/src/test/postfach'; -import { createFilledSettingsListResource } from '../../test/settings'; -import { getPostfachResource } from './settings.util'; +import { Resource } from '@ngxp/rest'; +import { createFilledSettingsListResource, createSettingItemResource } from '../../test/settings'; +import { SettingItemResource, SettingListResource, SettingName } from './settings.model'; +import { getSettingItemResource } from './settings.util'; -describe('get postfach resource', () => { - it('should return state resource with postfach resource if exists', () => { - const postfachResource: PostfachResource = createPostfachResource(); +describe('get setting item resource', () => { + const settingName: SettingName = <SettingName>'DUMMY'; + + it('should return matching setting resource as state resource if exists', () => { + const settingItemResource: SettingItemResource<unknown> = { ...createSettingItemResource(), name: settingName }; const settingsListResource: StateResource<SettingListResource> = createStateResource( - createFilledSettingsListResource([postfachResource]), + createFilledSettingsListResource([settingItemResource]), ); - const postfachStateResource: StateResource<PostfachResource> = getPostfachResource(settingsListResource); + const stateResource: StateResource<Resource> = getSettingItemResource(settingsListResource, settingName); - expect(postfachStateResource.resource).toEqual(postfachResource); + expect(stateResource.resource).toEqual(settingItemResource); }); - it('should return empty state resource if postfach resource NOT exists', () => { + it('should return empty state resource if no matching resource exists', () => { const settingsListResource: StateResource<SettingListResource> = createStateResource(createFilledSettingsListResource([])); - const postfachStateResource: StateResource<PostfachResource> = getPostfachResource(settingsListResource); + const stateResource: StateResource<Resource> = getSettingItemResource(settingsListResource, settingName); - expect(postfachStateResource).toEqual(createEmptyStateResource()); + expect(stateResource).toEqual(createEmptyStateResource()); }); }); diff --git a/alfa-client/libs/admin/settings-shared/src/lib/settings.util.ts b/alfa-client/libs/admin/settings-shared/src/lib/settings.util.ts index d5cf7605cada028e253a26d1e3641eee5856e2c7..2a00f86b2a84fef05b45ccfde06c3ff8a6321745 100644 --- a/alfa-client/libs/admin/settings-shared/src/lib/settings.util.ts +++ b/alfa-client/libs/admin/settings-shared/src/lib/settings.util.ts @@ -21,7 +21,6 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { PostfachResource } from '@admin-client/postfach-shared'; import { createEmptyStateResource, createStateResource, @@ -32,14 +31,11 @@ import { import { SettingListLinkRel } from './settings.linkrel'; import { SettingItemResource, SettingListResource, SettingName } from './settings.model'; -export function getPostfachResource(settingsListResource: StateResource<SettingListResource>): StateResource<PostfachResource> { - const entries: SettingItemResource[] = getEmbeddedResources(settingsListResource, SettingListLinkRel.LIST); - const postfachSettingItemResource: SettingItemResource = entries.find(isPostfachSettingItem); - return isNotNil(postfachSettingItemResource) ? - createStateResource(postfachSettingItemResource as PostfachResource) - : createEmptyStateResource(); -} - -function isPostfachSettingItem(item: SettingItemResource): boolean { - return item.name === SettingName.POSTFACH; +export function getSettingItemResource<T>( + settingsListResource: StateResource<SettingListResource>, + itemName: SettingName, +): StateResource<SettingItemResource<T>> { + const entries: SettingItemResource<T>[] = getEmbeddedResources(settingsListResource, SettingListLinkRel.LIST); + const entry: SettingItemResource<T> = entries.find((item: SettingItemResource<T>) => item.name === itemName); + return isNotNil(entry) ? createStateResource(entry) : createEmptyStateResource<SettingItemResource<T>>(); } diff --git a/alfa-client/libs/admin/settings-shared/test/settings.ts b/alfa-client/libs/admin/settings-shared/test/settings.ts index c3b9478ea57d373b18ad6315c0e5161fd7d9b008..a7bf1ded5bac477efcce579bd74f1646d8664a2d 100644 --- a/alfa-client/libs/admin/settings-shared/test/settings.ts +++ b/alfa-client/libs/admin/settings-shared/test/settings.ts @@ -1,9 +1,10 @@ +import { faker } from '@faker-js/faker/.'; import { Resource } from '@ngxp/rest'; import { toResource } from 'libs/tech-shared/test/resource'; import { SettingListLinkRel } from '../src/lib/settings.linkrel'; import { SettingItemResource, SettingListResource } from '../src/lib/settings.model'; -export function createSettingsListResource(settingsItems: SettingItemResource[]): SettingListResource { +export function createSettingsListResource(settingsItems: SettingItemResource<unknown>[]): SettingListResource { return toResource({}, [], { settings: settingsItems, }); @@ -14,3 +15,10 @@ export function createFilledSettingsListResource(resources: Resource[], linkRela [SettingListLinkRel.LIST]: resources, }); } + +export function createSettingItemResource(): SettingItemResource<unknown> { + return toResource({ + name: faker.word.sample(), + settingBody: {}, + }); +} diff --git a/alfa-client/libs/admin/shared/src/index.ts b/alfa-client/libs/admin/shared/src/index.ts index ece2c025dc3e05d4b26ad131fc7e419331e301d2..8f0992f8376dfb02cbd0096e33f516ac819f3d97 100644 --- a/alfa-client/libs/admin/shared/src/index.ts +++ b/alfa-client/libs/admin/shared/src/index.ts @@ -1 +1,4 @@ +export * from './lib/admin-cancel-button/admin-cancel-button.component'; +export * from './lib/admin-save-button/admin-save-button.component'; export * from './lib/routes'; +export * from './lib/token'; diff --git a/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.html b/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.html new file mode 100644 index 0000000000000000000000000000000000000000..3125fbdfd9242e48e3a30a5891e445bb6d8b8498 --- /dev/null +++ b/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.html @@ -0,0 +1,3 @@ +<ods-routing-button [linkPath]="linkPath" text="Abbrechen" variant="outline" dataTestId="cancel-button"> + <ods-close-icon icon class="fill-primary" /> +</ods-routing-button> diff --git a/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.spec.ts b/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..253650b230fb8be30ddb854fe3ad62351b058e5b --- /dev/null +++ b/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.spec.ts @@ -0,0 +1,36 @@ +import { AdminCancelButtonComponent } from '@admin-client/shared'; +import { getMockComponent } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { RoutingButtonComponent } from '@ods/component'; +import { CloseIconComponent } from '@ods/system'; +import { MockComponent } from 'ng-mocks'; + +describe('AdminCancelButtonComponent', () => { + let component: AdminCancelButtonComponent; + let fixture: ComponentFixture<AdminCancelButtonComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AdminCancelButtonComponent], + declarations: [MockComponent(RoutingButtonComponent), MockComponent(CloseIconComponent)], + }).compileComponents(); + + fixture = TestBed.createComponent(AdminCancelButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('button', () => { + it('should be called with linkPath', () => { + component.linkPath = 'dummyLinkPath'; + + fixture.detectChanges(); + + expect(getMockComponent(fixture, RoutingButtonComponent).linkPath).toBe('dummyLinkPath'); + }); + }); +}); diff --git a/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.ts b/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..8c284f05c61e3d545025c8447d4d302a0fc3db85 --- /dev/null +++ b/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.ts @@ -0,0 +1,14 @@ +import { CommonModule } from '@angular/common'; +import { Component, Input } from '@angular/core'; +import { RoutingButtonComponent } from '@ods/component'; +import { CloseIconComponent } from '@ods/system'; + +@Component({ + selector: 'admin-cancel-button', + templateUrl: './admin-cancel-button.component.html', + standalone: true, + imports: [CommonModule, RoutingButtonComponent, CloseIconComponent], +}) +export class AdminCancelButtonComponent { + @Input() linkPath: string; +} diff --git a/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.html b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.html new file mode 100644 index 0000000000000000000000000000000000000000..8bd6a5f92932e22006b3b8e2cb353a9c3fa93eb1 --- /dev/null +++ b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.html @@ -0,0 +1,6 @@ +<ods-button-with-spinner + (clickEmitter)="submit()" + text="Speichern" + dataTestId="save-button" + [stateResource]="stateResource$ | async" +/> diff --git a/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.spec.ts b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..52a303f378e824677dd6518126ae838ebd416e3a --- /dev/null +++ b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.spec.ts @@ -0,0 +1,71 @@ +import { ADMIN_FORMSERVICE } from '@admin-client/shared'; +import { AbstractFormService, createStateResource, StateResource } from '@alfa-client/tech-shared'; +import { dispatchEventFromFixture, getMockComponent, Mock, MockEvent } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { Resource } from '@ngxp/rest'; +import { ButtonWithSpinnerComponent } from '@ods/component'; +import { getDataTestIdAttributeOf } from 'libs/tech-shared/test/data-test'; +import { singleCold } from 'libs/tech-shared/test/marbles'; +import { createDummyResource } from 'libs/tech-shared/test/resource'; +import { MockComponent } from 'ng-mocks'; +import { of } from 'rxjs'; +import { AdminSaveButtonComponent } from './admin-save-button.component'; + +describe('AdminSaveButtonComponent', () => { + let component: AdminSaveButtonComponent; + let fixture: ComponentFixture<AdminSaveButtonComponent>; + + let formService: Mock<AbstractFormService<Resource>>; + + const saveButton: string = getDataTestIdAttributeOf('save-button'); + + const stateResource: StateResource<Resource> = createStateResource(createDummyResource()); + + beforeEach(async () => { + formService = <any>{ submit: jest.fn().mockReturnValue(singleCold(stateResource)) }; + + await TestBed.configureTestingModule({ + imports: [AdminSaveButtonComponent], + declarations: [MockComponent(ButtonWithSpinnerComponent)], + providers: [ + { + provide: ADMIN_FORMSERVICE, + useValue: formService, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(AdminSaveButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('on submit', () => { + it('should call formService', () => { + dispatchEventFromFixture(fixture, saveButton, MockEvent.CLICK); + + expect(formService.submit).toHaveBeenCalled(); + }); + + it('should assign state resource', () => { + dispatchEventFromFixture(fixture, saveButton, MockEvent.CLICK); + + expect(component.stateResource$).toBeObservable(singleCold(stateResource)); + }); + }); + + describe('button', () => { + it('should call with stateResource', () => { + component.stateResource$ = of(stateResource); + + fixture.detectChanges(); + + const comp: ButtonWithSpinnerComponent = getMockComponent(fixture, ButtonWithSpinnerComponent); + expect(comp.stateResource).toBe(stateResource); + }); + }); +}); diff --git a/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.ts b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..60abd08c7f311ecabe6d56ecc47fbfcd16412877 --- /dev/null +++ b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.ts @@ -0,0 +1,23 @@ +import { ADMIN_FORMSERVICE } from '@admin-client/shared'; +import { AbstractFormService, createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; +import { CommonModule } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { Resource } from '@ngxp/rest'; +import { ButtonWithSpinnerComponent } from '@ods/component'; +import { Observable, of } from 'rxjs'; + +@Component({ + selector: 'admin-save-button', + standalone: true, + imports: [CommonModule, ButtonWithSpinnerComponent], + templateUrl: './admin-save-button.component.html', +}) +export class AdminSaveButtonComponent { + private formService: AbstractFormService<Resource> = inject(ADMIN_FORMSERVICE); + + public stateResource$: Observable<StateResource<Resource>> = of(createEmptyStateResource<Resource>()); + + public submit(): void { + this.stateResource$ = this.formService.submit(); + } +} diff --git a/alfa-client/libs/admin/shared/src/lib/token.ts b/alfa-client/libs/admin/shared/src/lib/token.ts new file mode 100644 index 0000000000000000000000000000000000000000..f19e2c875946591ba08470c654f91c80664176aa --- /dev/null +++ b/alfa-client/libs/admin/shared/src/lib/token.ts @@ -0,0 +1,5 @@ +import { AbstractFormService } from '@alfa-client/tech-shared'; +import { InjectionToken } from '@angular/core'; +import { Resource } from '@ngxp/rest'; + +export const ADMIN_FORMSERVICE = new InjectionToken<AbstractFormService<Resource>>('adminFormService'); diff --git a/alfa-client/libs/admin/statistik/src/index.ts b/alfa-client/libs/admin/statistik/src/index.ts index cdc5d7b64f24d50920b721f3ab01c5b69cb41b99..157271feb3bf8625864f12faec9623307c3b7690 100644 --- a/alfa-client/libs/admin/statistik/src/index.ts +++ b/alfa-client/libs/admin/statistik/src/index.ts @@ -1 +1,2 @@ export * from './lib/statistik-container/statistik-container.component'; +export * from './lib/statistik-fields-form/admin-statistik-fields-form.component'; diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.html b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.html index 77c10ae229f5d8c6430d82349d6f2d213fa72054..1fd0441a39d71372d0c65fd84d0ce5386ac52fd2 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.html +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.html @@ -24,10 +24,12 @@ --> <h1 class="heading-1" data-test-id="statistik-header-text">Statistik</h1> -<div class="mt-4"> - <ods-button +<div class="mt-4 w-fit"> + <ods-routing-button + [linkPath]="ROUTES.STATISTIK_NEU" text="Weitere Felder auswerten" - (clickEmitter)="navigateToStatistikFieldsForm()" dataTestId="weitere-felder-auswerten-button" - ></ods-button> + ></ods-routing-button> </div> + +<ng-container *ngIf="listStateResource$ | async"></ng-container> diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.spec.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.spec.ts index b4390c03face2557262e05f6964e02dc40e4cd7a..a2bc7e4bfe5efdec1af21b9ad1da7deaccf1a21f 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.spec.ts +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.spec.ts @@ -21,11 +21,15 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { ROUTES } from '@admin-client/shared'; -import { NavigationService } from '@alfa-client/navigation-shared'; -import { existsAsHtmlElement, mock, Mock, triggerEvent } from '@alfa-client/test-utils'; +import { AggregationMappingListResource, AggregationMappingService } from '@admin-client/reporting-shared'; +import { createStateResource, StateResource } from '@alfa-client/tech-shared'; +import { existsAsHtmlElement, mock, Mock } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { RoutingButtonComponent } from '@ods/component'; +import { singleCold } from 'libs/tech-shared/test/marbles'; +import { MockComponent } from 'ng-mocks'; import { getDataTestIdAttributeOf } from '../../../../../tech-shared/test/data-test'; +import { createAggregationMappingListResource } from '../../../../reporting-shared/test/aggregation-mapping'; import { StatistikContainerComponent } from './statistik-container.component'; describe('StatistikContainerComponent', () => { @@ -34,43 +38,34 @@ describe('StatistikContainerComponent', () => { const evaluateAdditionalFieldsTestId: string = getDataTestIdAttributeOf('weitere-felder-auswerten-button'); - let navigationService: Mock<NavigationService>; - - beforeEach(() => { - navigationService = mock(NavigationService); - }); + let aggregationMappingService: Mock<AggregationMappingService>; beforeEach(async () => { + aggregationMappingService = mock(AggregationMappingService); + await TestBed.configureTestingModule({ imports: [StatistikContainerComponent], - providers: [ - { - provide: NavigationService, - useValue: navigationService, + declarations: [MockComponent(RoutingButtonComponent)], + }) + .overrideComponent(StatistikContainerComponent, { + set: { + providers: [ + { + provide: AggregationMappingService, + useValue: aggregationMappingService, + }, + ], }, - ], - }); - }); + }) + .compileComponents(); - beforeEach(() => { fixture = TestBed.createComponent(StatistikContainerComponent); component = fixture.componentInstance; - fixture.detectChanges(); }); - describe('component', () => { - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('navigateToStatistikFieldsForm', () => { - it('should call navigation service', () => { - component.navigateToStatistikFieldsForm(); - - expect(navigationService.navigate).toHaveBeenCalledWith(ROUTES.STATISTIK_NEU); - }); - }); + it('should create', () => { + expect(component).toBeTruthy(); }); describe('template', () => { @@ -80,23 +75,28 @@ describe('StatistikContainerComponent', () => { existsAsHtmlElement(fixture, evaluateAdditionalFieldsTestId); }); + }); + }); - describe('output', () => { - describe('clickEmitter', () => { - it('should call handler', () => { - component.navigateToStatistikFieldsForm = jest.fn(); - fixture.detectChanges(); + describe('on init', () => { + const stateResource: StateResource<AggregationMappingListResource> = createStateResource( + createAggregationMappingListResource(), + ); - triggerEvent({ - fixture, - elementSelector: evaluateAdditionalFieldsTestId, - name: 'clickEmitter', - }); + beforeEach(() => { + aggregationMappingService.getList.mockReturnValue(singleCold(stateResource)); + }); - expect(component.navigateToStatistikFieldsForm).toHaveBeenCalled(); - }); - }); - }); + it('should call service to get list', () => { + component.ngOnInit(); + + expect(aggregationMappingService.getList).toHaveBeenCalled(); + }); + + it('should assign stateResource', () => { + component.ngOnInit(); + + expect(component.listStateResource$).toBeObservable(singleCold(stateResource)); }); }); }); diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.ts index a00051de29e86232f267891b6f6c8324a726993a..90c3515896348b1c1d6ee77ba4f247428e79c4bf 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.ts +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.ts @@ -21,22 +21,29 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { AggregationMappingListResource, AggregationMappingService } from '@admin-client/reporting-shared'; import { ROUTES } from '@admin-client/shared'; -import { NavigationService } from '@alfa-client/navigation-shared'; +import { StateResource } from '@alfa-client/tech-shared'; import { CommonModule } from '@angular/common'; -import { Component, inject } from '@angular/core'; -import { ButtonComponent } from '@ods/system'; +import { Component, inject, OnInit } from '@angular/core'; +import { RoutingButtonComponent } from '@ods/component'; +import { Observable } from 'rxjs'; @Component({ selector: 'admin-statistik-container', templateUrl: './statistik-container.component.html', standalone: true, - imports: [CommonModule, ButtonComponent], + imports: [CommonModule, RoutingButtonComponent], + providers: [AggregationMappingService], }) -export class StatistikContainerComponent { - private readonly navigationService = inject(NavigationService); +export class StatistikContainerComponent implements OnInit { + private service = inject(AggregationMappingService); - public navigateToStatistikFieldsForm(): void { - this.navigationService.navigate(ROUTES.STATISTIK_NEU); + public listStateResource$: Observable<StateResource<AggregationMappingListResource>>; + + public readonly ROUTES = ROUTES; + + ngOnInit(): void { + this.listStateResource$ = this.service.getList(); } } diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.html b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.html index cef302fc15f148000041c0c968f1ccbefc446e83..27805c87fe074bae41311083bc1aed20d5aaeaef 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.html +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.html @@ -1,37 +1,29 @@ <h2 class="heading-2" data-test-id="statistik-fields-form-header-text">Felder zur Auswertung hinzufügen</h2> <div class="flex max-w-4xl flex-col gap-4"> - <ods-text-input - [fieldControl]="formEngineFormControl" - label="Formengine" - placeholder="Tragen Sie hier die Formengine des Formulars ein." - data-test-id="form-engine-input" - ></ods-text-input> - - <ods-text-input - [fieldControl]="formIdFormControl" - label="FormID" - placeholder="Tragen Sie hier die FormID des Formulars ein." - data-test-id="form-id-input" - ></ods-text-input> - - @for (dataFieldControl of dataFieldsFormControls; track $index) { - <ods-text-input - [fieldControl]="dataFieldControl" - label="Pfad des Datenfeldes" - placeholder="Tragen Sie hier den gesamten Pfad des Datenfeldes ein, das Sie auswerten möchten." - [attr.data-test-id]="'data-statistik-field-' + $index" - ></ods-text-input> - } - - <ods-button text="Datenfeld hinzufügen" (clickEmitter)="addDataField()" dataTestId="add-data-field-button"> - <ods-plus-icon icon class="fill-whitetext"/> + <form class="form flex-col" [formGroup]="formService.form" class="flex flex-col gap-4"> + <div [formGroupName]="StatistikFieldsFormService.FIELD_FORM_IDENTIFIER" class="flex flex-col gap-4"> + <ods-text-editor + [formControlName]="StatistikFieldsFormService.FIELD_FORM_ENGINE_NAME" + label="Formengine" + placeholder="Tragen Sie hier die Formengine des Formulars ein." + data-test-id="form-engine-name-input" + ></ods-text-editor> + <ods-text-editor + [formControlName]="StatistikFieldsFormService.FIELD_FORM_ID" + label="FormID" + placeholder="Tragen Sie hier die FormID des Formulars ein." + data-test-id="form-id-input" + ></ods-text-editor> + </div> + <statistik-fields-form-mapping /> + </form> + <ods-button text="Datenfeld hinzufügen" dataTestId="add-mapping-button" (clickEmitter)="formService.addMapping()"> + <ods-plus-icon icon class="fill-whitetext" /> </ods-button> <div class="mt-4 flex gap-4"> - <ods-button text="Speichern" dataTestId="save-statistik-fields-button"></ods-button> - <ods-button text="Abbrechen" variant="outline" (clickEmitter)="onCancel()" dataTestId="cancel-statistik-fields-button"> - <ods-close-icon icon class="fill-primary"/> - </ods-button> + <admin-save-button /> + <admin-cancel-button [linkPath]="Routes.STATISTIK" /> </div> </div> diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.spec.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.spec.ts index 54502497cd68edcdec0876e2aad378126c2c28e3..a0154fd3137315cfbfd12187d70f0ffb43635df5 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.spec.ts +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.spec.ts @@ -1,86 +1,75 @@ -import { ROUTES } from '@admin-client/shared'; -import { NavigationService } from '@alfa-client/navigation-shared'; -import { existsAsHtmlElement, getElementComponentFromFixtureByCss, mock, Mock, triggerEvent } from '@alfa-client/test-utils'; +import { ADMIN_FORMSERVICE } from '@admin-client/shared'; +import { EMPTY_STRING } from '@alfa-client/tech-shared'; +import { dispatchEventFromFixture, existsAsHtmlElement, mock, Mock, MockEvent } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { FormBuilder } from '@angular/forms'; -import { TextInputComponent } from '@ods/system'; +import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { TextEditorComponent } from '@ods/component'; +import { ButtonComponent, PlusIconComponent } from '@ods/system'; +import { MockComponent } from 'ng-mocks'; import { getDataTestIdAttributeOf, getDataTestIdOf } from '../../../../../tech-shared/test/data-test'; +import { AdminCancelButtonComponent } from '../../../../shared/src/lib/admin-cancel-button/admin-cancel-button.component'; +import { AdminSaveButtonComponent } from '../../../../shared/src/lib/admin-save-button/admin-save-button.component'; import { AdminStatistikFieldsFormComponent } from './admin-statistik-fields-form.component'; +import { AdminStatistikFieldsFormMappingComponent } from './statistik-fields-form-mapping/statistik-fields-form-mapping.component'; import { StatistikFieldsFormService } from './statistik-fields.formservice'; describe('AdminStatistikFieldsFormComponent', () => { let component: AdminStatistikFieldsFormComponent; let fixture: ComponentFixture<AdminStatistikFieldsFormComponent>; - const formEngineInputTestId: string = getDataTestIdOf('form-engine-input'); + const formEngineNameInputTestId: string = getDataTestIdOf('form-engine-name-input'); const formIdInputTestId: string = getDataTestIdOf('form-id-input'); - const addDataFieldButtonTestId: string = getDataTestIdAttributeOf('add-data-field-button'); - const saveButtonTestId: string = getDataTestIdAttributeOf('save-statistik-fields-button'); - const cancelButtonTestId: string = getDataTestIdAttributeOf('cancel-statistik-fields-button'); - const dataField1TestId: string = getDataTestIdOf('data-statistik-field-0'); + const addMappingButton: string = getDataTestIdAttributeOf('add-mapping-button'); - let formService: StatistikFieldsFormService; - let navigationService: Mock<NavigationService>; + const formBuilder: FormBuilder = new FormBuilder(); - beforeEach(() => { - formService = new StatistikFieldsFormService(new FormBuilder()); - navigationService = mock(NavigationService); - }); + let formService: Mock<StatistikFieldsFormService>; beforeEach(async () => { - TestBed.overrideComponent(AdminStatistikFieldsFormComponent, { - set: { - providers: [ - { - provide: StatistikFieldsFormService, - useValue: formService, - }, - { - provide: NavigationService, - useValue: navigationService, - }, - ], - }, + const form: FormGroup = formBuilder.group({ + [StatistikFieldsFormService.FIELD_FORM_IDENTIFIER]: formBuilder.group({ + [StatistikFieldsFormService.FIELD_FORM_ENGINE_NAME]: new FormControl(EMPTY_STRING), + [StatistikFieldsFormService.FIELD_FORM_ID]: new FormControl(EMPTY_STRING), + }), }); + formService = <any>{ ...mock(StatistikFieldsFormService), form }; + await TestBed.configureTestingModule({ - imports: [AdminStatistikFieldsFormComponent], - providers: [{ provide: StatistikFieldsFormService, useValue: formService }], - }).compileComponents(); + declarations: [ + AdminStatistikFieldsFormComponent, + MockComponent(TextEditorComponent), + MockComponent(ButtonComponent), + MockComponent(PlusIconComponent), + MockComponent(AdminSaveButtonComponent), + MockComponent(AdminCancelButtonComponent), + MockComponent(AdminStatistikFieldsFormMappingComponent), + ], + imports: [ReactiveFormsModule], + }) + .overrideComponent(AdminStatistikFieldsFormComponent, { + set: { + providers: [ + { + provide: StatistikFieldsFormService, + useValue: formService, + }, + { + provide: ADMIN_FORMSERVICE, + useValue: formService, + }, + ], + }, + }) + .compileComponents(); fixture = TestBed.createComponent(AdminStatistikFieldsFormComponent); component = fixture.componentInstance; fixture.detectChanges(); }); - describe('component', () => { - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should set form controls', () => { - expect(component.formEngineFormControl).toBeDefined(); - expect(component.formIdFormControl).toBeDefined(); - expect(component.dataFieldsFormControls).toBeDefined(); - }); - - describe('addDataField', () => { - it('should call form service', () => { - formService.addDataField = jest.fn(); - - component.addDataField(); - - expect(formService.addDataField).toHaveBeenCalled(); - }); - }); - - describe('onCancel', () => { - it('should call navigation service', () => { - component.onCancel(); - - expect(navigationService.navigate).toHaveBeenCalledWith(ROUTES.STATISTIK); - }); - }); + it('should create', () => { + expect(component).toBeTruthy(); }); describe('template', () => { @@ -88,18 +77,7 @@ describe('AdminStatistikFieldsFormComponent', () => { it('should exists', () => { fixture.detectChanges(); - existsAsHtmlElement(fixture, formEngineInputTestId); - }); - - it('should have been called with inputs', () => { - fixture.detectChanges(); - - const formEngineInput: TextInputComponent = getElementComponentFromFixtureByCss<TextInputComponent>( - fixture, - formEngineInputTestId, - ); - - expect(formEngineInput.fieldControl).toEqual(component.formEngineFormControl); + existsAsHtmlElement(fixture, formEngineNameInputTestId); }); }); @@ -109,91 +87,23 @@ describe('AdminStatistikFieldsFormComponent', () => { existsAsHtmlElement(fixture, formIdInputTestId); }); - - it('should have been called with inputs', () => { - fixture.detectChanges(); - - const formIdInput: TextInputComponent = getElementComponentFromFixtureByCss<TextInputComponent>( - fixture, - formIdInputTestId, - ); - - expect(formIdInput.fieldControl).toEqual(component.formIdFormControl); - }); - }); - - describe('data field input', () => { - it('should exists', () => { - fixture.detectChanges(); - - existsAsHtmlElement(fixture, dataField1TestId); - }); - - it('should have been called with inputs', () => { - fixture.detectChanges(); - - const dataFieldInput: TextInputComponent = getElementComponentFromFixtureByCss<TextInputComponent>( - fixture, - dataField1TestId, - ); - - expect(dataFieldInput.fieldControl).toEqual(component.dataFieldsFormControls[0]); - }); - }); - - describe('add data field button', () => { - it('should exists', () => { - fixture.detectChanges(); - - existsAsHtmlElement(fixture, addDataFieldButtonTestId); - }); - - describe('output', () => { - describe('clickEmitter', () => { - it('should call handler', () => { - fixture.detectChanges(); - component.addDataField = jest.fn(); - - triggerEvent({ - fixture, - elementSelector: addDataFieldButtonTestId, - name: 'clickEmitter', - }); - - expect(component.addDataField).toHaveBeenCalled(); - }); - }); - }); - }); - - describe('save button', () => { - it('should exists', () => { - fixture.detectChanges(); - - existsAsHtmlElement(fixture, saveButtonTestId); - }); }); - describe('cancel button', () => { + describe('add mapping button', () => { it('should exists', () => { fixture.detectChanges(); - existsAsHtmlElement(fixture, cancelButtonTestId); + existsAsHtmlElement(fixture, addMappingButton); }); describe('output', () => { describe('clickEmitter', () => { - it('should call handler', () => { - component.onCancel = jest.fn(); + it('should call formService', () => { fixture.detectChanges(); - triggerEvent({ - fixture, - elementSelector: cancelButtonTestId, - name: 'clickEmitter', - }); + dispatchEventFromFixture(fixture, addMappingButton, MockEvent.CLICK); - expect(component.onCancel).toHaveBeenCalled(); + expect(formService.addMapping).toHaveBeenCalled(); }); }); }); diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.ts index 59d083f34a2f5f727734c29c02f6b8de13c7d2d6..0298196202e623a3d080468c792f3c1c800a8750 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.ts +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.ts @@ -1,37 +1,30 @@ -import { ROUTES } from '@admin-client/shared'; -import { NavigationService } from '@alfa-client/navigation-shared'; -import { CommonModule } from '@angular/common'; +import { ADMIN_FORMSERVICE, AdminCancelButtonComponent, AdminSaveButtonComponent, ROUTES } from '@admin-client/shared'; import { Component, inject } from '@angular/core'; -import { FormArray, FormControl, ReactiveFormsModule } from '@angular/forms'; -import { ButtonComponent, CloseIconComponent, PlusIconComponent, TextInputComponent } from '@ods/system'; +import { ReactiveFormsModule } from '@angular/forms'; +import { TextEditorComponent } from '@ods/component'; +import { ButtonComponent, DeleteIconComponent, PlusIconComponent } from '@ods/system'; +import { AdminStatistikFieldsFormMappingComponent } from './statistik-fields-form-mapping/statistik-fields-form-mapping.component'; import { StatistikFieldsFormService } from './statistik-fields.formservice'; @Component({ selector: 'admin-statistik-fields-form', - standalone: true, - imports: [CommonModule, TextInputComponent, ButtonComponent, CloseIconComponent, ReactiveFormsModule, PlusIconComponent], - providers: [StatistikFieldsFormService], templateUrl: './admin-statistik-fields-form.component.html', + standalone: true, + imports: [ + ButtonComponent, + PlusIconComponent, + ReactiveFormsModule, + TextEditorComponent, + DeleteIconComponent, + AdminSaveButtonComponent, + AdminCancelButtonComponent, + AdminStatistikFieldsFormMappingComponent, + ], + providers: [{ provide: ADMIN_FORMSERVICE, useClass: StatistikFieldsFormService }], }) export class AdminStatistikFieldsFormComponent { - private readonly formService = inject(StatistikFieldsFormService); - private readonly navigationService = inject(NavigationService); - - public readonly formEngineFormControl: FormControl = this.formService.form.controls[ - StatistikFieldsFormService.FIELD_FORM_ENGINE - ] as FormControl; - public readonly formIdFormControl: FormControl = this.formService.form.controls[ - StatistikFieldsFormService.FIELD_FORM_ID - ] as FormControl; - public readonly dataFieldsFormControls: FormControl[] = ( - this.formService.form.controls[StatistikFieldsFormService.FIELD_DATA_FIELDS] as FormArray - ).controls as FormControl[]; - - public addDataField(): void { - this.formService.addDataField(); - } + public readonly formService = <StatistikFieldsFormService>inject(ADMIN_FORMSERVICE); - public onCancel(): void { - this.navigationService.navigate(ROUTES.STATISTIK); - } + public readonly StatistikFieldsFormService = StatistikFieldsFormService; + public readonly Routes = ROUTES; } diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.html b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.html new file mode 100644 index 0000000000000000000000000000000000000000..2ce86b545b1039b795911cf172f154b9e2b97871 --- /dev/null +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.html @@ -0,0 +1,27 @@ +<form [formGroup]="formService.form"> + <div class="flex flex-col gap-4" [formArrayName]="StatistikFieldsFormService.FIELD_MAPPINGS"> + <div + *ngFor="let mappingControl of formService.mappings.controls; let i = index" + [formGroupName]="i" + class="flex w-full gap-2" + > + <ods-text-editor + class="flex-1" + formControlName="sourcePath" + label="Pfad des Datenfeldes" + placeholder="Tragen Sie hier den gesamten Pfad des Datenfeldes ein, das Sie auswerten möchten." + [attr.data-test-id]="'mapping-field-' + i" + ></ods-text-editor> + <ods-button + class="self-end" + variant="ghost" + size="fit" + destructive="true" + (clickEmitter)="formService.removeMapping(i)" + [dataTestId]="'remove-mapping-button-' + i" + > + <ods-delete-icon icon /> + </ods-button> + </div> + </div> +</form> diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.spec.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..61cd006e429951cde3657be7688b30063cc2e6eb --- /dev/null +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.spec.ts @@ -0,0 +1,90 @@ +import { ADMIN_FORMSERVICE } from '@admin-client/shared'; +import { EMPTY_STRING } from '@alfa-client/tech-shared'; +import { dispatchEventFromFixture, existsAsHtmlElement, mock, Mock, MockEvent, mockGetValue } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { TextEditorComponent } from '@ods/component'; +import { ButtonComponent, DeleteIconComponent } from '@ods/system'; +import { getDataTestIdOf, getDynamicDataTestIdAttributOf } from 'libs/tech-shared/test/data-test'; +import { MockComponent } from 'ng-mocks'; +import { StatistikFieldsFormService } from '../statistik-fields.formservice'; +import { AdminStatistikFieldsFormMappingComponent } from './statistik-fields-form-mapping.component'; + +describe('AdminStatistikFieldsFormMappingComponent', () => { + let component: AdminStatistikFieldsFormMappingComponent; + let fixture: ComponentFixture<AdminStatistikFieldsFormMappingComponent>; + + const mappingField: string = getDataTestIdOf('mapping-field-0'); + const removeMappingButton: string = getDynamicDataTestIdAttributOf('remove-mapping-button-0'); + + const formBuilder: FormBuilder = new FormBuilder(); + + let formService: Mock<StatistikFieldsFormService>; + + beforeEach(async () => { + const form: FormGroup = formBuilder.group({ + [StatistikFieldsFormService.FIELD_MAPPINGS]: formBuilder.array([ + new FormGroup({ sourcePath: new FormControl(EMPTY_STRING) }), + ]), + }); + + formService = <any>{ + ...mock(StatistikFieldsFormService), + form, + addMapping: jest.fn(), + removeMapping: jest.fn(), + }; + + mockGetValue( + formService, + StatistikFieldsFormService.FIELD_MAPPINGS, + form.controls[StatistikFieldsFormService.FIELD_MAPPINGS], + ); + + await TestBed.configureTestingModule({ + declarations: [ + AdminStatistikFieldsFormMappingComponent, + MockComponent(TextEditorComponent), + MockComponent(ButtonComponent), + MockComponent(DeleteIconComponent), + ], + imports: [ReactiveFormsModule], + providers: [ + { + provide: StatistikFieldsFormService, + useValue: formService, + }, + { + provide: ADMIN_FORMSERVICE, + useValue: formService, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(AdminStatistikFieldsFormMappingComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('template', () => { + describe('mapping input', () => { + it('should exists', () => { + fixture.detectChanges(); + + existsAsHtmlElement(fixture, mappingField); + }); + }); + + describe('remove mapping button', () => { + it('should call formservice', () => { + dispatchEventFromFixture(fixture, removeMappingButton, MockEvent.CLICK); + + expect(formService.removeMapping).toHaveBeenCalledWith(0); + }); + }); + }); +}); diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..48bc3eaddd2a2fe51b983939dd8920b02e7f8dae --- /dev/null +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.ts @@ -0,0 +1,29 @@ +import { ADMIN_FORMSERVICE, AdminCancelButtonComponent, AdminSaveButtonComponent, ROUTES } from '@admin-client/shared'; +import { CommonModule } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { TextEditorComponent } from '@ods/component'; +import { ButtonComponent, DeleteIconComponent, PlusIconComponent } from '@ods/system'; +import { StatistikFieldsFormService } from '../statistik-fields.formservice'; + +@Component({ + selector: 'statistik-fields-form-mapping', + templateUrl: './statistik-fields-form-mapping.component.html', + standalone: true, + imports: [ + CommonModule, + ButtonComponent, + PlusIconComponent, + ReactiveFormsModule, + TextEditorComponent, + DeleteIconComponent, + AdminSaveButtonComponent, + AdminCancelButtonComponent, + ], +}) +export class AdminStatistikFieldsFormMappingComponent { + public readonly formService = <StatistikFieldsFormService>inject(ADMIN_FORMSERVICE); + + public readonly StatistikFieldsFormService = StatistikFieldsFormService; + public readonly Routes = ROUTES; +} diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields.formservice.spec.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields.formservice.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..58a2273cb692762c4f930e583c8c49f210099517 --- /dev/null +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields.formservice.spec.ts @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +import { AggregationMappingResource, AggregationMappingService } from '@admin-client/reporting-shared'; +import { EMPTY_STRING, StateResource, createStateResource } from '@alfa-client/tech-shared'; +import { Mock, mock } from '@alfa-client/test-utils'; +import { TestBed } from '@angular/core/testing'; +import { FormArray, FormControl, FormGroup } from '@angular/forms'; +import { createAggregationMapping, createAggregationMappingResource } from 'libs/admin/reporting-shared/test/aggregation-mapping'; +import { of } from 'rxjs'; +import { StatistikFieldsFormService } from './statistik-fields.formservice'; + +describe('StatistikFieldsFormService', () => { + let formService: StatistikFieldsFormService; + + let service: Mock<AggregationMappingService>; + + beforeEach(() => { + service = mock(AggregationMappingService); + + TestBed.configureTestingModule({ + providers: [StatistikFieldsFormService, { provide: AggregationMappingService, useValue: service }], + }); + + formService = TestBed.inject(StatistikFieldsFormService); + }); + + it('should create', () => { + expect(formService).toBeTruthy(); + }); + + describe('on do submit', () => { + const stateResource: StateResource<AggregationMappingResource> = createStateResource(createAggregationMappingResource()); + + beforeEach(() => { + service.create.mockReturnValue(of(stateResource)); + }); + + it('should call service', () => { + formService.form = <any>createAggregationMapping(); + + formService.submit(); + + expect(service.create).toHaveBeenCalledWith(formService.form.value); + }); + }); + + describe('add mapping', () => { + it('should add mapping control', () => { + formService.addMapping(); + + const mappingFormArray: FormArray = <FormArray>formService.form.controls[StatistikFieldsFormService.FIELD_MAPPINGS]; + expect(mappingFormArray).toHaveLength(2); + expect(mappingFormArray.controls[0].value).toEqual({ sourcePath: EMPTY_STRING }); + }); + }); + + describe('remove mapping', () => { + it('should remove mapping control', () => { + (<FormArray>formService.form.controls[StatistikFieldsFormService.FIELD_MAPPINGS]).push( + new FormGroup({ sourcePath: new FormControl('controlToRemove') }), + ); + + formService.removeMapping(1); + + const mappingFormArray: FormArray = <FormArray>formService.form.controls[StatistikFieldsFormService.FIELD_MAPPINGS]; + expect(mappingFormArray).toHaveLength(1); + expect(mappingFormArray.controls[0].value).toEqual({ sourcePath: EMPTY_STRING }); + }); + }); + + describe('get mappings', () => { + it('should return mappings as array', () => { + const mappings: FormArray = formService.mappings; + + expect(mappings).toHaveLength(1); + expect(mappings.controls[0].value).toEqual({ sourcePath: EMPTY_STRING }); + }); + }); +}); diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields.formservice.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields.formservice.ts index 229a7147d1b785c937d46c45c3ae65070a8b2608..1b746e12f42b44cc1261ce6de543380ea61d9ec4 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields.formservice.ts +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields.formservice.ts @@ -1,32 +1,50 @@ +import { AggregationMappingResource, AggregationMappingService } from '@admin-client/reporting-shared'; import { AbstractFormService, EMPTY_STRING, StateResource } from '@alfa-client/tech-shared'; -import { Injectable } from '@angular/core'; -import { FormArray, FormControl, UntypedFormGroup } from '@angular/forms'; -import { Resource } from '@ngxp/rest'; -import { EMPTY, Observable } from 'rxjs'; +import { inject, Injectable } from '@angular/core'; +import { FormArray, FormControl, FormGroup, UntypedFormGroup } from '@angular/forms'; +import { Observable } from 'rxjs'; @Injectable() -export class StatistikFieldsFormService extends AbstractFormService<Resource> { - public static readonly FIELD_FORM_ENGINE: string = 'formEngine'; +export class StatistikFieldsFormService extends AbstractFormService<AggregationMappingResource> { + private service = inject(AggregationMappingService); + + public static readonly FIELD_FORM_IDENTIFIER: string = 'formIdentifier'; + public static readonly FIELD_FORM_ENGINE_NAME: string = 'formEngineName'; public static readonly FIELD_FORM_ID: string = 'formId'; - public static readonly FIELD_DATA_FIELDS: string = 'dataFields'; + + public static readonly FIELD_MAPPINGS: string = 'mappings'; protected initForm(): UntypedFormGroup { return this.formBuilder.group({ - [StatistikFieldsFormService.FIELD_FORM_ENGINE]: new FormControl(EMPTY_STRING), - [StatistikFieldsFormService.FIELD_FORM_ID]: new FormControl(EMPTY_STRING), - [StatistikFieldsFormService.FIELD_DATA_FIELDS]: new FormArray([new FormControl(EMPTY_STRING)]), + [StatistikFieldsFormService.FIELD_FORM_IDENTIFIER]: this.formBuilder.group({ + [StatistikFieldsFormService.FIELD_FORM_ENGINE_NAME]: new FormControl(EMPTY_STRING), + [StatistikFieldsFormService.FIELD_FORM_ID]: new FormControl(EMPTY_STRING), + }), + [StatistikFieldsFormService.FIELD_MAPPINGS]: new FormArray([this.createArrayControl()]), }); } - protected doSubmit(): Observable<StateResource<Resource>> { - return EMPTY; + protected doSubmit(): Observable<StateResource<AggregationMappingResource>> { + return this.service.create(this.getFormValue()); } protected getPathPrefix(): string { return 'settingBody'; } - public addDataField(): void { - (this.form.controls[StatistikFieldsFormService.FIELD_DATA_FIELDS] as FormArray).push(new FormControl(EMPTY_STRING)); + public addMapping(): void { + this.mappings.push(this.createArrayControl()); + } + + private createArrayControl(): FormGroup { + return new FormGroup({ sourcePath: new FormControl(EMPTY_STRING) }); + } + + public removeMapping(index: number): void { + this.mappings.removeAt(index); + } + + public get mappings(): FormArray { + return this.form.controls[StatistikFieldsFormService.FIELD_MAPPINGS] as FormArray; } } diff --git a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.html b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.html index bffb14da6bda5e9ae083f25f445a12b3d6854ae5..153e3e19deecd0653b83ae9fc7e516f85b6a8716 100644 --- a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.html +++ b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.html @@ -24,9 +24,9 @@ --> <h1 class="heading-1 mb-4">Benutzer & Rollen</h1> -<ods-routing-button [linkPath]="ROUTES.BENUTZER_NEU" text="Benutzer hinzufügen" class="mb-4" dataTestId="add-user-button" /> +<ods-routing-button [linkPath]="ROUTES.BENUTZER_NEU" text="Benutzer hinzufügen" class="mb-4 w-fit" dataTestId="add-user-button" /> <ods-list> @for (user of usersStateResource.resource; track $index) { <admin-user [user]="user" class="block w-full" /> } -</ods-list> \ No newline at end of file +</ods-list> diff --git a/alfa-client/libs/authentication/src/lib/http-unauthorized.interceptor.spec.ts b/alfa-client/libs/authentication/src/lib/http-unauthorized.interceptor.spec.ts index bf426ac9299c13c5440929e9b54d276e35f6be8e..8e32931929aab5f151b00cc4b14b3aaacc5c9142 100644 --- a/alfa-client/libs/authentication/src/lib/http-unauthorized.interceptor.spec.ts +++ b/alfa-client/libs/authentication/src/lib/http-unauthorized.interceptor.spec.ts @@ -22,12 +22,12 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { Mock, mock } from '@alfa-client/test-utils'; +import { HttpErrorResponse, HttpHandler, HttpRequest } from '@angular/common/http'; import { TestBed } from '@angular/core/testing'; import { MatDialogModule } from '@angular/material/dialog'; -import { HttpUnauthorizedInterceptor } from './http-unauthorized.interceptor'; -import { HttpErrorResponse, HttpHandler, HttpRequest } from '@angular/common/http'; import { Subject, isEmpty } from 'rxjs'; import { AuthenticationService } from './authentication.service'; +import { HttpUnauthorizedInterceptor } from './http-unauthorized.interceptor'; describe('HttpUnauthorizedInterceptor', () => { let interceptor: HttpUnauthorizedInterceptor; @@ -96,7 +96,7 @@ describe('HttpUnauthorizedInterceptor', () => { it('should rethrow error if not unauthorized status', () => { const anyError: any = new HttpErrorResponse({ status: 500 }); - expect(() => interceptor.handleError(anyError)).toThrowError(); + expect(() => interceptor.handleError(anyError)).toThrow(); }); }); }); diff --git a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts index d799453d615d41bd0e21b7940461d59c7131adf0..ab845cf6b4f7e3156ebb7a940e418780e9a410d2 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts @@ -23,6 +23,7 @@ */ import { BinaryFileListResource, BinaryFileResource, BinaryFileService } from '@alfa-client/binary-file-shared'; import { CommandOrder, CommandResource, CommandService, CreateCommandProps } from '@alfa-client/command-shared'; +import { PostfachService } from '@alfa-client/postfach-shared'; import { ApiError, EMPTY_ARRAY, @@ -80,7 +81,6 @@ import { BescheidService } from './bescheid.service'; import { DocumentLinkRel } from './document.linkrel'; import { DocumentResource } from './document.model'; -import { PostfachService } from '@alfa-client/postfach-shared'; import * as DateUtil from '../../../tech-shared/src/lib/date.util'; import * as BescheidUtil from './bescheid.util'; @@ -89,7 +89,7 @@ describe('BescheidService', () => { let facade: Mock<BescheidFacade>; let vorgangService: Mock<VorgangService>; - let resourceRepository: Mock<ResourceRepository>; + let resourceRepository: Mock<ResourceRepository<BescheidResource>>; let commandService: Mock<CommandService>; let vorgangCommandService: Mock<VorgangCommandService>; let binaryFileService: Mock<BinaryFileService>; diff --git a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts index 3426c36b0868b332b4324580b8eadf6fcbab37f0..7b8a2f737d688302a3a9f30e218b126a9fad285e 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts @@ -124,7 +124,7 @@ export class BescheidService { private readonly commandService: CommandService, private readonly vorgangCommandService: VorgangCommandService, private readonly binaryFileService: BinaryFileService, - private readonly repository: ResourceRepository, + private readonly repository: ResourceRepository<BescheidResource>, private readonly postfachService: PostfachService, ) { this.bescheidResourceService = new CommandResourceService( @@ -132,7 +132,11 @@ export class BescheidService { repository, this.commandService, ); - this.bescheidListResourceService = new ResourceListService(this.buildBescheidListServiceConfig(), repository); + this.bescheidListResourceService = new ResourceListService< + VorgangWithEingangResource, + BescheidListResource, + BescheidResource + >(this.buildBescheidListServiceConfig(), repository); } public getActiveStep(): Observable<BescheidWizardStep> { diff --git a/alfa-client/libs/collaboration/src/lib/collaboration-in-vorgang-container/collaboration-request-form/collaboration.request.formservice.spec.ts b/alfa-client/libs/collaboration/src/lib/collaboration-in-vorgang-container/collaboration-request-form/collaboration.request.formservice.spec.ts index 9af26540fdf4031d434bcb3d4000e2a3f56d3c96..d5726c77529ec251188d2323565ad95742986a58 100644 --- a/alfa-client/libs/collaboration/src/lib/collaboration-in-vorgang-container/collaboration-request-form/collaboration.request.formservice.spec.ts +++ b/alfa-client/libs/collaboration/src/lib/collaboration-in-vorgang-container/collaboration-request-form/collaboration.request.formservice.spec.ts @@ -22,11 +22,11 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { CollaborationListResource } from '@alfa-client/collaboration-shared'; -import { SnackBarService } from '@alfa-client/ui'; -import { TestBed } from '@angular/core/testing'; import { CommandLinkRel, CommandResource } from '@alfa-client/command-shared'; import { StateResource, createEmptyStateResource, createStateResource } from '@alfa-client/tech-shared'; -import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; +import { Mock, mock } from '@alfa-client/test-utils'; +import { SnackBarService } from '@alfa-client/ui'; +import { TestBed } from '@angular/core/testing'; import { UntypedFormBuilder } from '@angular/forms'; import { CollaborationService } from 'libs/collaboration-shared/src/lib/collaboration.service'; import { createCollaborationListResource } from 'libs/collaboration-shared/test/collaboration'; @@ -57,7 +57,6 @@ describe('CollaborationRequestFormService', () => { }); formService = TestBed.inject(CollaborationRequestFormService); - TestBed.inject(CollaborationRequestFormService); }); it('should create', () => { diff --git a/alfa-client/libs/command-shared/src/lib/command-resource.service.spec.ts b/alfa-client/libs/command-shared/src/lib/command-resource.service.spec.ts index 200b8572a2ed3faccb5d14a2346e085ca8262024..c7640cb221008d78f73dd470ae9d3f10287d4bcb 100644 --- a/alfa-client/libs/command-shared/src/lib/command-resource.service.spec.ts +++ b/alfa-client/libs/command-shared/src/lib/command-resource.service.spec.ts @@ -74,13 +74,13 @@ describe('CommandResourceService', () => { describe('doSave', () => { it('should throw error', () => { - expect(() => service.doSave(configResource, {})).toThrowError('Method not implemented.'); + expect(() => service.doSave(configResource, {})).toThrow('Method not implemented.'); }); }); describe('doPatch', () => { it('should throw error', () => { - expect(() => service.doPatch(configResource, {})).toThrowError('Method not implemented.'); + expect(() => service.doPatch(configResource, {})).toThrow('Method not implemented.'); }); }); diff --git a/alfa-client/libs/design-component/src/lib/routing-button/routing-button.component.ts b/alfa-client/libs/design-component/src/lib/routing-button/routing-button.component.ts index 2a25cd22e5df3e74e404a3fd986d737b29ab5a2c..98346f3903e1cff4b363c5f4f4dc49243ce84c7d 100644 --- a/alfa-client/libs/design-component/src/lib/routing-button/routing-button.component.ts +++ b/alfa-client/libs/design-component/src/lib/routing-button/routing-button.component.ts @@ -1,22 +1,26 @@ import { CommonModule } from '@angular/common'; import { Component, Input } from '@angular/core'; import { RouterLink } from '@angular/router'; +import { ButtonVariants, buttonVariants } from '@ods/system'; @Component({ selector: 'ods-routing-button', standalone: true, imports: [CommonModule, RouterLink], host: { class: 'block' }, - template: `<a - [routerLink]="'/' + linkPath" - [attr.data-test-id]="dataTestId" - class="block min-h-9 w-fit min-w-32 rounded bg-primary px-4 py-2 text-sm font-medium text-white shadow-sm outline-focus hover:bg-primary-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2" - > - {{ text }} + template: `<a [routerLink]="'/' + linkPath" [attr.data-test-id]="dataTestId" [ngClass]="buttonVariants({ size, variant })"> + <ng-content select="[icon]" /> + @if (text) { + <p class="flex-grow">{{ text }}</p> + } </a>`, }) export class RoutingButtonComponent { @Input() linkPath: string; @Input() text: string; @Input() dataTestId: string; + @Input() variant: ButtonVariants['variant']; + @Input() size: ButtonVariants['size']; + + readonly buttonVariants = buttonVariants; } diff --git a/alfa-client/libs/design-system/src/index.ts b/alfa-client/libs/design-system/src/index.ts index 467032047c06e4ae9c4a6e3948cc13a704b8761d..e7ae662925633f6c656ce0c64ba919debf94b774 100644 --- a/alfa-client/libs/design-system/src/index.ts +++ b/alfa-client/libs/design-system/src/index.ts @@ -52,6 +52,7 @@ export * from './lib/icons/bescheid-upload-icon/bescheid-upload-icon.component'; export * from './lib/icons/check-circle-icon/check-circle-icon.component'; export * from './lib/icons/check-icon/check-icon.component'; export * from './lib/icons/close-icon/close-icon.component'; +export * from './lib/icons/delete-icon/delete-icon.component'; export * from './lib/icons/delete-vorgang-finally-icon/delete-vorgang-finally-icon.component'; export * from './lib/icons/discard-vorgang-icon/discard-vorgang-icon.component'; export * from './lib/icons/edit-icon/edit-icon.component'; diff --git a/alfa-client/libs/design-system/src/lib/button/button.component.ts b/alfa-client/libs/design-system/src/lib/button/button.component.ts index 46b0308c205dff5a889142e76693dd27cffc30bb..15a9b7efdc261b462f3dade0f93b2a6902e36507 100644 --- a/alfa-client/libs/design-system/src/lib/button/button.component.ts +++ b/alfa-client/libs/design-system/src/lib/button/button.component.ts @@ -64,22 +64,24 @@ export const buttonVariants = cva( { variant: 'primary', destructive: true, - class: '[&]:hover:enabled:bg-destructive-primary-hover [&]:bg-destructive [&]:outline-destructive', + class: + '[&]:hover:enabled:bg-destructive-primary-hover [&]:bg-destructive [&]:outline-destructive [&]:focus-visible:bg-destructive-primary-hover', }, { variant: 'outline', destructive: true, - class: '[&]:border-destructive [&]:text-destructive [&]:hover:enabled:bg-destructive-hover', + class: + '[&]:border-destructive [&]:text-destructive [&]:hover:enabled:bg-destructive-hover [&]:focus-visible:bg-destructive-hover', }, { variant: 'ghost', destructive: true, - class: '[&]:text-destructive [&]:hover:enabled:bg-destructive-hover', + class: '[&]:text-destructive [&]:hover:enabled:bg-destructive-hover [&]:focus-visible:bg-destructive-hover', }, ], }, ); -type ButtonVariants = VariantProps<typeof buttonVariants>; +export type ButtonVariants = VariantProps<typeof buttonVariants>; @Component({ selector: 'ods-button', diff --git a/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts b/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts index 5de77014fc3466b2a8a5768e2f003c61347d9dad..ca6a007c19e49ebb481ff0655b407cc3076a9c08 100644 --- a/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts +++ b/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts @@ -26,6 +26,7 @@ import { CommonModule } from '@angular/common'; import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { cva, VariantProps } from 'class-variance-authority'; +import { uniqueId } from 'lodash-es'; const textInputVariants = cva( [ @@ -86,7 +87,7 @@ export class TextInputComponent { @Input({ required: true }) set label(label: string) { this.inputLabel = label; - this.id = convertForDataTest(label); + this.id = `${convertForDataTest(label)}-${uniqueId()}`; } @Input() placeholder: string = ''; @Input() autocomplete: string = 'off'; diff --git a/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.component.spec.ts b/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..354449bdfb0652606ec23dee7b970a5d793fd292 --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { DeleteIconComponent } from './delete-icon.component'; + +describe('DeleteIconComponent', () => { + let component: DeleteIconComponent; + let fixture: ComponentFixture<DeleteIconComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DeleteIconComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(DeleteIconComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc06f683c6790f219c7c08fb258764b35cdf2b6c --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.component.ts @@ -0,0 +1,27 @@ +import { CommonModule } from '@angular/common'; +import { Component, Input } from '@angular/core'; +import { twMerge } from 'tailwind-merge'; +import { IconVariants, iconVariants } from '../iconVariants'; + +@Component({ + selector: 'ods-delete-icon', + standalone: true, + imports: [CommonModule], + template: `<svg + viewBox="0 0 24 24" + xmlns="http://www.w3.org/2000/svg" + [ngClass]="[twMerge(iconVariants({ size }), 'fill-error', class)]" + aria-hidden="true" + > + <path + d="M7 21C6.45 21 5.97917 20.8042 5.5875 20.4125C5.19583 20.0208 5 19.55 5 19V6H4V4H9V3H15V4H20V6H19V19C19 19.55 18.8042 20.0208 18.4125 20.4125C18.0208 20.8042 17.55 21 17 21H7ZM17 6H7V19H17V6ZM9 17H11V8H9V17ZM13 17H15V8H13V17Z" + /> + </svg> `, +}) +export class DeleteIconComponent { + @Input() size: IconVariants['size'] = 'medium'; + @Input() class: string = ''; + + readonly iconVariants = iconVariants; + readonly twMerge = twMerge; +} diff --git a/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.stories.ts b/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.stories.ts new file mode 100644 index 0000000000000000000000000000000000000000..4602289bc57c1be551e4bf2ba29003ab4bd7de31 --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.stories.ts @@ -0,0 +1,27 @@ +import type { Meta, StoryObj } from '@storybook/angular'; + +import { DeleteIconComponent } from './delete-icon.component'; + +const meta: Meta<DeleteIconComponent> = { + title: 'Icons/Delete icon', + component: DeleteIconComponent, + excludeStories: /.*Data$/, + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj<DeleteIconComponent>; + +export const Default: Story = { + args: { size: 'large' }, + argTypes: { + size: { + control: 'select', + options: ['small', 'medium', 'large', 'extra-large', 'full'], + description: 'Size of icon. Property "full" means 100%', + table: { + defaultValue: { summary: 'medium' }, + }, + }, + }, +}; diff --git a/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.spec.ts b/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.spec.ts index 02e4ff7ccc5ffa9a28b0295d44991618862758bb..60c4640d92c10a70d0bec7d763d80a8f9618ff33 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.spec.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.spec.ts @@ -29,7 +29,7 @@ import { cold } from 'jest-marbles'; import { DummyLinkRel, DummyListLinkRel } from 'libs/tech-shared/test/dummy'; import { createDummyListResource, createDummyResource, createFilledDummyListResource } from 'libs/tech-shared/test/resource'; import { BehaviorSubject, Observable, of } from 'rxjs'; -import { singleCold, singleHot } from '../../../test/marbles'; +import { multipleCold, singleCold, singleHot } from '../../../test/marbles'; import { EMPTY_ARRAY } from '../tech.util'; import { ResourceListService } from './list-resource.service'; import { CreateResourceData, LinkRelationName, ListItemResource, ListResourceServiceConfig } from './resource.model'; @@ -311,25 +311,27 @@ describe('ListResourceService', () => { describe('create', () => { const toCreate: unknown = {}; + const returnResource: Resource = createDummyResource(); beforeEach(() => { service.listResource.value.resource = listResource; + resourceRepository.createResource.mockReturnValue(of(returnResource)); }); it('should throw error if link is not present', () => { service.listResource.next(createStateResource(createDummyListResource())); - expect(() => service.create(toCreate)).toThrowError('No creation link exists.'); + expect(() => service.create(toCreate)).toThrow('No creation link exists.'); }); it('should throw error if list resource is empty', () => { service.listResource.next(createEmptyStateResource()); - expect(() => service.create(toCreate)).toThrowError('No list resource available.'); + expect(() => service.create(toCreate)).toThrow('No list resource available.'); }); it('should trigger resourceLoader', () => { - service.create(toCreate); + service.create(toCreate).subscribe(); const createResourceData: CreateResourceData<ListResource> = { resource: listResource, @@ -341,11 +343,11 @@ describe('ListResourceService', () => { it('should return value', () => { const returnResource: Resource = createDummyResource(); - resourceRepository.createResource.mockReturnValue(singleHot(returnResource)); + resourceRepository.createResource.mockReturnValue(singleCold(returnResource, '-a')); - const createdResource: Observable<Resource> = service.create(toCreate); + const createdResource: Observable<StateResource<Resource>> = service.create(toCreate); - expect(createdResource).toBeObservable(singleCold(returnResource)); + expect(createdResource).toBeObservable(multipleCold(createEmptyStateResource(true), createStateResource(returnResource))); }); }); @@ -443,7 +445,7 @@ describe('ListResourceService', () => { it('should throw error if prev link is not present', () => { service.listResource.next(createStateResource(listResource)); - expect(() => service.prev()).toThrowError('There is no previous page.'); + expect(() => service.prev()).toThrow('There is no previous page.'); }); it('should call resourceLoader with prev link', () => { @@ -471,7 +473,7 @@ describe('ListResourceService', () => { it('should throw error if next link is not present', () => { service.listResource.next(createStateResource(listResource)); - expect(() => service.next()).toThrowError('There is no next page.'); + expect(() => service.next()).toThrow('There is no next page.'); }); it('should call resourceLoader with next link', () => { diff --git a/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.ts b/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.ts index 13df5d4cf6fa8f06588a096d454e598faa8a4fa3..852a8299c56d256d93bf778e9600ab0717edc00f 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.ts @@ -58,7 +58,7 @@ export class ResourceListService<B extends Resource, T extends ListResource, I e constructor( private config: ListResourceServiceConfig<B>, - private repository: ResourceRepository, + private repository: ResourceRepository<I>, ) {} public getList(): Observable<StateResource<T>> { @@ -106,9 +106,12 @@ export class ResourceListService<B extends Resource, T extends ListResource, I e this.listResource.next(createEmptyStateResource()); } - public create(toCreate: unknown): Observable<Resource> { + public create(toCreate: unknown): Observable<StateResource<I>> { this.verifyBeforeCreation(); - return this.repository.createResource(this.buildCreateResourceData(toCreate, this.config.createLinkRel)); + return this.repository.createResource(this.buildCreateResourceData(toCreate, this.config.createLinkRel)).pipe( + map((listItemResource: I) => createStateResource(listItemResource)), + startWith(createEmptyStateResource<I>(true)), + ); } private verifyBeforeCreation(): void { diff --git a/alfa-client/libs/tech-shared/src/lib/resource/resource.repository.ts b/alfa-client/libs/tech-shared/src/lib/resource/resource.repository.ts index 8a4081f31be113eaf2986e867f294603a9d56a6a..33d37b87af453f7fb777dd2ca69d8cf7a8f394c4 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/resource.repository.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/resource.repository.ts @@ -28,7 +28,7 @@ import { CreateResourceData, LinkRelationName, SaveResourceData } from './resour import { ListResource } from './resource.util'; @Injectable({ providedIn: 'root' }) -export class ResourceRepository { +export class ResourceRepository<T extends Resource = Resource> { private resourceFactory = inject(ResourceFactory); static SEARCH_PARAM: string = 'searchBy'; @@ -45,7 +45,7 @@ export class ResourceRepository { return uri; } - public createResource(createResourceData: CreateResourceData<Resource>): Observable<Resource> { + public createResource(createResourceData: CreateResourceData<Resource>): Observable<T> { return this.resourceFactory.from(createResourceData.resource).post(createResourceData.linkRel, createResourceData.toCreate); } diff --git a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts index d7d188b8c526a9d439b934397b54c32e2332227a..87869c5c4e0aa1dff4a3050b95ed26abd476c0d6 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts @@ -503,7 +503,7 @@ describe('ResourceService', () => { const thrownError$: Observable<StateResource<unknown>> = service.handleError(error); expect.assertions(1); - expect(lastValueFrom(thrownError$)).rejects.toThrowError('Internal Server Error'); + expect(lastValueFrom(thrownError$)).rejects.toThrow('Internal Server Error'); }); }); diff --git a/alfa-client/libs/tech-shared/test/data-test.ts b/alfa-client/libs/tech-shared/test/data-test.ts index 98db6b1f6fb58fa31cc9353c8440c442bbfb4112..795b2dee30c083f71da5842c2cc64738c3dd9da4 100644 --- a/alfa-client/libs/tech-shared/test/data-test.ts +++ b/alfa-client/libs/tech-shared/test/data-test.ts @@ -38,3 +38,7 @@ export function getDataTestIdOf(value: string): string { export function getDataTestIdAttributeOf(value: string): string { return `[dataTestId="${value}"]`; } + +export function getDynamicDataTestIdAttributOf(value: string): string { + return `[ng-reflect-data-test-id="${value}"]`; +} diff --git a/alfa-client/libs/tech-shared/test/marbles.ts b/alfa-client/libs/tech-shared/test/marbles.ts index ea8e04237504cc8104ddb569ce574dd66c6ad362..fecda8769f38a80dd34c693c9ca39bf175a69eef 100644 --- a/alfa-client/libs/tech-shared/test/marbles.ts +++ b/alfa-client/libs/tech-shared/test/marbles.ts @@ -34,3 +34,7 @@ export function singleCold(object: any, frame: string = 'a'): ObservableWithSubs export function singleColdCompleted(object: any, frame: string = 'a'): ObservableWithSubscriptions { return cold(`(${frame}|)`, { a: object }); } + +export function multipleCold(first: any, second: any): ObservableWithSubscriptions { + return cold('ab', { a: first, b: second }); +} diff --git a/alfa-client/libs/test-utils/src/lib/mocking.ts b/alfa-client/libs/test-utils/src/lib/mocking.ts index a5e3137a17d3fed7c81cd72e350599195cd079f7..0fb616db60420994880e9acfddec1d0d37e10486 100644 --- a/alfa-client/libs/test-utils/src/lib/mocking.ts +++ b/alfa-client/libs/test-utils/src/lib/mocking.ts @@ -47,3 +47,9 @@ export function mockClass(clazz: any): Mock<any> { export function createSpy(clazz: any, functionName: string) { return jest.spyOn(clazz.prototype, functionName); } + +export function mockGetValue(object: any, name: string, returnValue: any): void { + Object.defineProperty(object, name, { + get: jest.fn(() => returnValue), + }); +} diff --git a/alfa-client/libs/test-utils/src/lib/model.ts b/alfa-client/libs/test-utils/src/lib/model.ts index d89ed415513a8528908c72641e609bc43c86c7c7..1094ac96376b5a8d2d81cf5a7ebf13c69aa2a550 100644 --- a/alfa-client/libs/test-utils/src/lib/model.ts +++ b/alfa-client/libs/test-utils/src/lib/model.ts @@ -29,3 +29,7 @@ export interface EventData<T> { name: string; data?: unknown; } + +export const MockEvent = { + CLICK: 'clickEmitter', +}; diff --git a/alfa-client/tsconfig.base.json b/alfa-client/tsconfig.base.json index 6850f74e4eb7c031308a264a51e8a4782fa9613b..f6bb4c8b2657d8acef026b997d32794971be5656 100644 --- a/alfa-client/tsconfig.base.json +++ b/alfa-client/tsconfig.base.json @@ -22,6 +22,7 @@ "@admin-client/organisations-einheit-shared": ["libs/admin/organisations-einheit-shared/src/index.ts"], "@admin-client/postfach": ["libs/admin/postfach/src/index.ts"], "@admin-client/postfach-shared": ["libs/admin/postfach-shared/src/index.ts"], + "@admin-client/reporting-shared": ["libs/admin/reporting-shared/src/index.ts"], "@admin-client/settings-shared": ["libs/admin/settings-shared/src/index.ts"], "@admin-client/shared": ["libs/admin/shared/src/index.ts"], "@admin-client/statistik": ["libs/admin/statistik/src/index.ts"],