Skip to content

Commit

Permalink
[Feat]: Notification Setting Page (#2712)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Sebastian Leidig <[email protected]>
  • Loading branch information
Ayush8923 and sleidig authored Jan 3, 2025
1 parent 1f70eda commit d4b7627
Show file tree
Hide file tree
Showing 61 changed files with 1,055 additions and 250 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
</div>

<app-view-actions>
<app-dialog-buttons [form]="form.formGroup" [entity]="entity">
<app-dialog-buttons [form]="form" [entity]="entity">
<button
mat-menu-item
[appExportData]="[entity]"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { NgForOf, NgIf } from "@angular/common";
import { MatTabsModule } from "@angular/material/tabs";
import { FaIconComponent } from "@fortawesome/angular-fontawesome";
import { MatButtonModule } from "@angular/material/button";
import { EntityTypeLabelPipe } from "../../../common-components/entity-type-label/entity-type-label.pipe";
import { ViewTitleComponent } from "../../../common-components/view-title/view-title.component";
import { AdminSectionHeaderComponent } from "../../building-blocks/admin-section-header/admin-section-header.component";
import { AdminEntityFormComponent } from "../admin-entity-form/admin-entity-form.component";
Expand All @@ -31,7 +30,6 @@ import { AdminTabTemplateDirective } from "../../building-blocks/admin-tabs/admi
MatTabsModule,
FaIconComponent,
MatButtonModule,
EntityTypeLabelPipe,
ViewTitleComponent,
AdminSectionHeaderComponent,
AdminEntityFormComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
>
<!-- GROUP HEADER -->
<app-admin-section-header
[(title)]="group.header"
[title]="group.header"
(titleChange)="group.header = $event; emitUpdatedConfig()"
(remove)="removeGroup(i)"
></app-admin-section-header>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@ export class AdminEntityFormComponent implements OnChanges {

this._config = value;
}

get config(): FormConfig {
return this._config;
}

private _config: FormConfig;

@Output() configChange = new EventEmitter<FormConfig>();
Expand Down Expand Up @@ -163,7 +165,7 @@ export class AdminEntityFormComponent implements OnChanges {
];
}

private emitUpdatedConfig() {
protected emitUpdatedConfig() {
this.configChange.emit(this.config);
}

Expand Down Expand Up @@ -291,6 +293,7 @@ export class AdminEntityFormComponent implements OnChanges {

this.emitUpdatedConfig();
}

/**
* drop handler specifically for the "create new Text field" item
* @param event
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ import {
ViewChild,
} from "@angular/core";
import { CommonModule } from "@angular/common";
import { FilterComponent } from "../../filter/filter/filter.component";
import { MatTab, MatTabGroup } from "@angular/material/tabs";
import { EntitiesTableComponent } from "../../common-components/entities-table/entities-table.component";
import { EntityConstructor } from "../../entity/model/entity";
import {
EntityListConfig,
Expand All @@ -29,7 +26,6 @@ import {
moveItemInArray,
} from "@angular/cdk/drag-drop";
import { FaIconComponent } from "@fortawesome/angular-fontawesome";
import { MatIconButton } from "@angular/material/button";
import { MatFormField, MatLabel } from "@angular/material/form-field";
import { MatSelect } from "@angular/material/select";
import { AdminTabsComponent } from "../building-blocks/admin-tabs/admin-tabs.component";
Expand All @@ -41,16 +37,11 @@ import { ViewTitleComponent } from "../../common-components/view-title/view-titl
standalone: true,
imports: [
CommonModule,
FilterComponent,
MatTabGroup,
EntitiesTableComponent,
MatTab,
EntityFieldsMenuComponent,
MatTableModule,
EntityFieldLabelComponent,
CdkDrag,
FaIconComponent,
MatIconButton,
CdkDropList,
MatFormField,
MatLabel,
Expand Down Expand Up @@ -92,11 +83,13 @@ export class AdminEntityListComponent implements OnChanges, AfterViewInit {
this.initAvailableFields();
}
}

ngAfterViewInit() {
const placeholderElement = this.placeholder.element.nativeElement;
placeholderElement.style.display = "none";
placeholderElement.parentNode.removeChild(placeholderElement);
}

/**
* Config allows to not have columnGroups and by default then display all `columns`,
* create an initial columnGroup in this case to allow full editing.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<div class="hint-banner" i18n>
You can share the link to a "Public Form" with people who do not have an
account or embed it in your website. People can submit new records through
this form without logging in. For example, you can let participants
self-register this way and then review their data later.
</div>

<app-view-title [disableBackButton]="true" [displayInPlace]="true" i18n>
Public Forms
</app-view-title>

<div>
<app-related-entities
entityType="PublicFormConfig"
property="entity"
[columns]="['title', 'route', 'description']"
[entity]="dummyEntity"
clickMode="popup-details"
></app-related-entities>
</div>
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { AdminEntityPublicFormsComponent } from "./admin-entity-public-forms-component";
import { MockedTestingModule } from "../../../utils/mocked-testing.module";
import { TestEntity } from "../../../utils/test-utils/TestEntity";

describe("AdminEntityPublicFormsComponent", () => {
let component: AdminEntityPublicFormsComponent;
let fixture: ComponentFixture<AdminEntityPublicFormsComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
AdminEntityPublicFormsComponent,
MockedTestingModule.withState(),
],
}).compileComponents();

fixture = TestBed.createComponent(AdminEntityPublicFormsComponent);
component = fixture.componentInstance;
component.entityConstructor = TestEntity;
});

it("should create the component", () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Component, Input } from "@angular/core";
import { EntityConstructor } from "app/core/entity/model/entity";
import { ViewTitleComponent } from "../../common-components/view-title/view-title.component";
import { RelatedEntitiesComponent } from "../../entity-details/related-entities/related-entities.component";

@Component({
selector: "app-admin-entity-public-forms-component",
standalone: true,
templateUrl: "./admin-entity-public-forms-component.html",
styleUrls: [
"./admin-entity-public-forms-component.scss",
"../admin-entity/admin-entity-styles.scss",
],
imports: [ViewTitleComponent, RelatedEntitiesComponent],
})
export class AdminEntityPublicFormsComponent {
/**
* The entity type for which to display public forms for.
*/
@Input() entityConstructor: EntityConstructor;

/**
* Fake entity instance to correctly filter/link related PublicFormConfigs
* using the standard related-entities component.
*/
protected dummyEntity: any = {
getId: () => this.entityConstructor.ENTITY_TYPE,
};
}
13 changes: 12 additions & 1 deletion src/app/core/admin/admin-entity.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
import { TestBed } from "@angular/core/testing";

import { AdminEntityService } from "./admin-entity.service";
import { EntityMapperService } from "../entity/entity-mapper/entity-mapper.service";
import { mockEntityMapper } from "../entity/entity-mapper/mock-entity-mapper-service";

describe("AdminEntityService", () => {
let service: AdminEntityService;

beforeEach(() => {
TestBed.configureTestingModule({});
TestBed.configureTestingModule({
providers: [
{
provide: EntityMapperService,
useValue: mockEntityMapper(),
},
],
});
service = TestBed.inject(AdminEntityService);
});

it("should be created", () => {
expect(service).toBeTruthy();
});

// saving tested via AdminEntityComponent (see admin-entity.component.spec.ts)
});
68 changes: 67 additions & 1 deletion src/app/core/admin/admin-entity.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { EventEmitter, Injectable } from "@angular/core";
import { EventEmitter, inject, Injectable } from "@angular/core";
import { EntityConstructor } from "../entity/model/entity";
import { Config } from "../config/config";
import { EntityConfig } from "../entity/entity-config";
import { EntityConfigService } from "../entity/entity-config.service";
import { EntityMapperService } from "../entity/entity-mapper/entity-mapper.service";
import { EntityListConfig } from "../entity-list/EntityListConfig";
import { EntityDetailsConfig } from "../entity-details/EntityDetailsConfig";
import { DynamicComponentConfig } from "../config/dynamic-components/dynamic-component-config.interface";

/**
* Simply service to centralize updates between various admin components in the form builder.
Expand All @@ -9,6 +16,7 @@ import { EntityConstructor } from "../entity/model/entity";
})
export class AdminEntityService {
public entitySchemaUpdated = new EventEmitter<void>();
private entityMapper = inject(EntityMapperService);

/**
* Set a new schema field to the given entity and trigger update event for related admin components.
Expand All @@ -24,4 +32,62 @@ export class AdminEntityService {
entityType.schema.set(fieldId, updatedEntitySchema);
this.entitySchemaUpdated.next();
}

/**
* Updates the EntityConfig in the database to take any in-memory changes
* of the EntityConstructor and persist them to the config doc.
*
* @param entityConstructor The entity type to be updated in the Config DB
* @param configEntitySettings (optional) general entity settings to also be applied
* @param configListView (optional) list view settings also to be applied
* @param configDetailsView (optional) details view settings also to be applied
*/
public async setAndSaveEntityConfig(
entityConstructor: EntityConstructor,
configEntitySettings?: EntityConfig,
configListView?: DynamicComponentConfig<EntityListConfig>,
configDetailsView?: DynamicComponentConfig<EntityDetailsConfig>,
): Promise<{ previous: Config; current: Config }> {
const originalConfig = await this.entityMapper.load(
Config,
Config.CONFIG_KEY,
);
const newConfig = originalConfig.copy();

let entitySchemaConfig: EntityConfig =
this.getEntitySchemaFromConfig(newConfig, entityConstructor) ?? {};
// Initialize config if not present
entitySchemaConfig.attributes = entitySchemaConfig.attributes ?? {};

for (const [fieldId, field] of entityConstructor.schema.entries()) {
entitySchemaConfig.attributes[fieldId] = field;
}

// Add additional general settings if available
if (configEntitySettings) {
Object.assign(entitySchemaConfig, configEntitySettings);
}

// Add additional view config if available
if (configListView) {
newConfig.data[EntityConfigService.getListViewId(entityConstructor)] =
configListView;
}
if (configDetailsView) {
newConfig.data[EntityConfigService.getDetailsViewId(entityConstructor)] =
configDetailsView;
}

const updatedConfig: Config = await this.entityMapper.save(newConfig);
return { previous: originalConfig, current: updatedConfig };
}

private getEntitySchemaFromConfig(
config: Config<unknown>,
entityConstructor: EntityConstructor,
): EntityConfig {
const entityConfigKey =
EntityConfigService.PREFIX_ENTITY_CONFIG + entityConstructor.ENTITY_TYPE;
return config.data[entityConfigKey];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { Entity, EntityConstructor } from "../../../entity/model/entity";
import { FaDynamicIconComponent } from "../../../common-components/fa-dynamic-icon/fa-dynamic-icon.component";
import { FontAwesomeTestingModule } from "@fortawesome/angular-fontawesome/testing";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { EntityMapperService } from "app/core/entity/entity-mapper/entity-mapper.service";
import { mockEntityMapper } from "app/core/entity/entity-mapper/mock-entity-mapper-service";

describe("AdminEntityGeneralSettingsComponent", () => {
let component: AdminEntityGeneralSettingsComponent;
Expand Down Expand Up @@ -41,6 +43,12 @@ describe("AdminEntityGeneralSettingsComponent", () => {
ReactiveFormsModule,
FormsModule,
],
providers: [
{
provide: EntityMapperService,
useValue: mockEntityMapper(),
},
],
}).compileComponents();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import { EntitySchemaField } from "app/core/entity/schema/entity-schema-field";
import { AdminEntityService } from "../../admin-entity.service";
import { StringDatatype } from "../../../basic-datatypes/string/string.datatype";
import { HelpButtonComponent } from "../../../common-components/help-button/help-button.component";
import { MatSort } from "@angular/material/sort";
import { EntityFieldLabelComponent } from "../../../common-components/entity-field-label/entity-field-label.component";
import { AnonymizeOptionsComponent } from "../../admin-entity-details/admin-entity-field/anonymize-options/anonymize-options.component";
import { FaIconComponent } from "@fortawesome/angular-fontawesome";

Expand Down Expand Up @@ -54,8 +52,6 @@ import { FaIconComponent } from "@fortawesome/angular-fontawesome";
CommonModule,
MatTooltipModule,
HelpButtonComponent,
MatSort,
EntityFieldLabelComponent,
AnonymizeOptionsComponent,
FaIconComponent,
],
Expand Down Expand Up @@ -162,6 +158,7 @@ export class AdminEntityGeneralSettingsComponent implements OnInit {
...unselectedOptions,
];
}

objectToLabel = (v: SimpleDropdownValue) => v?.label;
objectToValue = (v: SimpleDropdownValue) => v?.key;
}
Expand Down
13 changes: 12 additions & 1 deletion src/app/core/admin/admin-entity/admin-entity.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,13 @@
>
General Settings
</mat-list-item>

<mat-list-item
(click)="mode = 'publicForm'"
[activated]="mode === 'publicForm'"
i18n="admin entity: nav item"
>
Public Forms
</mat-list-item>
<app-beta-feature></app-beta-feature>
</mat-nav-list>

Expand Down Expand Up @@ -74,6 +80,11 @@
[(generalSettings)]="configEntitySettings"
></app-admin-entity-general-settings>
}
@case ("publicForm") {
<app-admin-entity-public-forms-component
[entityConstructor]="entityConstructor"
></app-admin-entity-public-forms-component>
}
}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,6 @@ describe("AdminEntityComponent", () => {
config: expectedViewConfig,
});
expect(actual.data[entityConfigId]).toEqual(expectedEntityConfig);
// TODO: this expectation is not useful yet:
expect(component.configEntitySettings).toEqual(component.entityConstructor);

// cleanup:
AdminTestEntity.schema.delete("testSaveField");
Expand Down
Loading

0 comments on commit d4b7627

Please sign in to comment.