Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

administer "pre-filled" values for Public Forms #2780

Draft
wants to merge 29 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
effc5cc
fix extra scrollbar
Abhinegi2 Dec 26, 2024
614528c
image centre allign
Abhinegi2 Dec 26, 2024
4a7fe2a
added migration to convert old coloumn config
Abhinegi2 Dec 26, 2024
32c09e5
Merge branch 'master' of https://github.com/Aam-Digital/ndb-core into…
Abhinegi2 Jan 2, 2025
bd445c8
added markdown guide + explanation
Abhinegi2 Jan 3, 2025
b9e78b1
updated formating for description text of public form
Abhinegi2 Jan 3, 2025
9febbe6
Merge branch 'master' into fix/public-form-imoprovements
Abhinegi2 Jan 3, 2025
8b67830
Merge branch 'master' of https://github.com/Aam-Digital/ndb-core into…
Abhinegi2 Jan 3, 2025
d3ae8e5
updated public form to support group header
Abhinegi2 Jan 3, 2025
230a0dc
Merge branch 'master' of https://github.com/Aam-Digital/ndb-core into…
Abhinegi2 Jan 3, 2025
b0b952b
cleanups
Abhinegi2 Jan 3, 2025
5b872a0
Merge branch 'master' into fix/public-form-imoprovements
Abhinegi2 Jan 4, 2025
5198b58
Merge branch 'master' into fix/public-form-imoprovements
Abhinegi2 Jan 6, 2025
8cb68f6
added new component
Abhinegi2 Jan 6, 2025
6e15419
added save functionality
Abhinegi2 Jan 8, 2025
731cfc5
Merge branch 'master' of https://github.com/Aam-Digital/ndb-core into…
Abhinegi2 Jan 8, 2025
bafa9c8
sync with master
Abhinegi2 Jan 8, 2025
8adc88b
code cleanups + styling added
Abhinegi2 Jan 8, 2025
d6c7563
added disable property
Abhinegi2 Jan 8, 2025
4f49b86
added test cases
Abhinegi2 Jan 8, 2025
2a2ae57
lint fixes
Abhinegi2 Jan 8, 2025
bf7f30d
code cleanups
Abhinegi2 Jan 8, 2025
20ce910
added restrictedprefilled
Abhinegi2 Jan 9, 2025
5d2a3b7
updated codebase to save prefilled config seperately
Abhinegi2 Jan 9, 2025
27dabda
lint fixes
Abhinegi2 Jan 9, 2025
97ed1e6
Merge branch 'master' into fix/added-prefilled-component
Abhinegi2 Jan 9, 2025
56121e6
updated publicform to support prefilled value
Abhinegi2 Jan 9, 2025
4ec0e8f
minor updates
Abhinegi2 Jan 9, 2025
b4a425c
minor updates
Abhinegi2 Jan 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<p i18n>
Set the default value related to the field. After setting the default value,
the selected value will be automatically pre-filled in the corresponding field
of the public form. This will apply only to this specific public form and will
not impact other forms or configurations.
</p>

<div class="defaultvalue-container" [class.disabled]="formControl.disabled">
<form [formGroup]="prefilledValueSettings">
<div formArrayName="prefilledvalue">
<div
*ngFor="let fieldGroup of prefilledValues.controls; let i = index"
[formGroupName]="i"
>
<div class="flex-row justify-space-between align-center container">
<div class="flex-row align-center width-1-2">
<mat-form-field>
<mat-label>Select Field</mat-label>
<mat-select formControlName="field" placeholder="Select a field">
<mat-option
*ngFor="let field of availableFields"
[value]="field"
>
{{ field }}
</mat-option>
</mat-select>
</mat-form-field>
<app-help-button
text="Select the field which you wanted to set default value."
i18n-text
></app-help-button>
</div>
<app-default-value-options
[value]="fieldGroup.get('defaultValue').value"
(valueChange)="fieldGroup.get('defaultValue').setValue($event)"
[entityType]="entityConstructor"
></app-default-value-options>

<button
mat-icon-button
(click)="removeRestrictedPrefilled(i)"
matTooltip="Remove defualt value"
i18n-matTooltip
>
<fa-icon icon="trash"></fa-icon>
</button>
</div>
</div>
</div>
</form>
<div class="add-new-defaultvalue-field">
<button
mat-stroked-button
class="add-new-defaultvalue-button"
color="accent"
(click)="addRestrictedPrefilled()"
matTooltip="Add another default value related to another field"
i18n-matTooltip
>
<fa-icon aria-label="add element" icon="plus-circle"></fa-icon>
<span i18n class="new-defaultvalue-rule">Add new notification rule</span>
</button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@use "variables/colors";
@use "variables/sizes";
@use "../../../../styles/variables/breakpoints";
@use "@angular/material/core/style/elevation" as mat-elevation;

.defaultvalue-container {
overflow: hidden
}
.defaultvalue-container.disabled {
opacity: 0.5;
pointer-events: none;
}

.container {
@include mat-elevation.elevation(2);
border-radius: 10px;
padding: 16px;
background-color: colors.$background;
margin-top: sizes.$regular;
gap: 40px;

@media screen and (min-width: breakpoints.$xxl) {
flex-direction: row;
}
}

.add-new-defaultvalue-field {
display: flex;
justify-content: center;
margin-top: 30px;
}

.add-new-defaultvalue-button {
width: 50%;
border-radius: 20px;
padding: 22px;
background-color: aliceblue;
}

.new-defaultvalue-rule {
margin-left: 5px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { EditPrefilledValuesComponent } from "./edit-prefilled-values.component";
import { EntityRegistry } from "app/core/entity/database-entity.decorator";
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatSelectModule } from "@angular/material/select";
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
import { MatButtonModule } from "@angular/material/button";
import { MatTooltipModule } from "@angular/material/tooltip";
import { FontAwesomeTestingModule } from "@fortawesome/angular-fontawesome/testing";
import { Entity } from "app/core/entity/model/entity";
import { TestEntity } from "app/utils/test-utils/TestEntity";

describe("EditPrefilledValuesComponent", () => {
let component: EditPrefilledValuesComponent;
let fixture: ComponentFixture<EditPrefilledValuesComponent>;
let mockEntityRegistry: Partial<EntityRegistry>;

beforeEach(async () => {
mockEntityRegistry = {
get: jasmine.createSpy("get").and.returnValue(Entity),
};

await TestBed.configureTestingModule({
imports: [
EditPrefilledValuesComponent,
ReactiveFormsModule,
MatFormFieldModule,
MatSelectModule,
MatTooltipModule,
FontAwesomeTestingModule,
MatButtonModule,
NoopAnimationsModule,
],
providers: [{ provide: EntityRegistry, useValue: mockEntityRegistry }],
}).compileComponents();

fixture = TestBed.createComponent(EditPrefilledValuesComponent);
component = fixture.componentInstance;
component.entity = new TestEntity();
component.formControl = new FormBuilder().control([]);
fixture.detectChanges();
});

it("should create the component", () => {
expect(component).toBeTruthy();
});

it("should add a new field to prefilled values", () => {
component.availableFields = ["name", "age"];
component.addRestrictedPrefilled();

expect(component.prefilledValues.length).toBe(1);
expect(component.prefilledValues.at(0).value).toEqual({
field: "",
defaultValue: { mode: "static", value: null },
hideFromForm: true,
});
});

it("should remove a field from prefilled values", () => {
component.prefilledValues.push(
new FormBuilder().group({
field: "name",
defaultValue: { mode: "static", value: "default name" },
hideFromForm: true,
}),
);

component.removeRestrictedPrefilled(0);

expect(component.prefilledValues.length).toBe(0);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import { CommonModule } from "@angular/common";
import { Component, OnInit, inject } from "@angular/core";
import { FormArray, FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { DefaultValueOptionsComponent } from "app/core/admin/admin-entity-details/admin-entity-field/default-value-options/default-value-options.component";
import { FieldGroup } from "app/core/entity-details/form/field-group";
import { EntityRegistry } from "app/core/entity/database-entity.decorator";
import { EditComponent } from "app/core/entity/default-datatype/edit-component";
import { EntityConstructor } from "app/core/entity/model/entity";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatSelectModule } from "@angular/material/select";
import { FontAwesomeModule } from "@fortawesome/angular-fontawesome";
import { FormConfig } from "app/core/entity-details/form/form.component";
import { DefaultValueConfig } from "app/core/entity/schema/default-value-config";
import { HelpButtonComponent } from "app/core/common-components/help-button/help-button.component";
import { MatButtonModule } from "@angular/material/button";
import { MatTooltipModule } from "@angular/material/tooltip";
import { FormFieldConfig } from "app/core/common-components/entity-form/FormConfig";

@Component({
selector: "app-edit-prefilled-values",
standalone: true,
imports: [
DefaultValueOptionsComponent,
CommonModule,
ReactiveFormsModule,
MatFormFieldModule,
MatSelectModule,
MatTooltipModule,
HelpButtonComponent,
FontAwesomeModule,
MatButtonModule,
],
templateUrl: "./edit-prefilled-values.component.html",
styleUrls: ["./edit-prefilled-values.component.scss"],
})
export class EditPrefilledValuesComponent
extends EditComponent<FieldGroup[]>
implements OnInit
{
entityConstructor: EntityConstructor;
formConfig: FormConfig;
availableFields: string[] = [];
defaultValue: DefaultValueConfig;
prefilledValueSettings = this.fb.group({
prefilledvalue: this.fb.array([]),
});

private entities = inject(EntityRegistry);

constructor(private fb: FormBuilder) {
super();
}

override ngOnInit(): void {
if (!this.entity) return;

this.entityConstructor = this.entities.get(this.entity["entity"]);
this.formConfig = { fieldGroups: this.formControl.getRawValue() };
this.populateAvailableFields();
this.initializePrefilledValues();
this.prefilledValueSettings.valueChanges.subscribe((value) =>
this.updateFieldGroups(value),
);
}

get prefilledValues(): FormArray {
return this.prefilledValueSettings.get("prefilledvalue") as FormArray;
}

private populateAvailableFields(): void {
this.availableFields = Array.from(this.entityConstructor.schema.entries())
.filter(([, value]) => value.label)
.sort(([, a], [, b]) => a.label.localeCompare(b.label))
.map(([key]) => key);
}

private initializePrefilledValues(): void {
if (!this.formConfig.fieldGroups) return;
this.formConfig.fieldGroups.forEach((group) => {
group.fields.forEach((field: FormFieldConfig) => {
const fieldId = typeof field === "string" ? field : field.id;
const defaultValue =
typeof field === "string" ? null : field.defaultValue;

if (fieldId) {
this.prefilledValues.push(
this.fb.group({
field: [fieldId],
defaultValue: [defaultValue],
hideFromForm: [field.hideFromForm ?? true],
}),
);
}
});
});
}

addRestrictedPrefilled(): void {
if (!this.availableFields.length) {
return;
}

this.prefilledValues.push(
this.fb.group({
field: [""],
defaultValue: {
mode: "static",
value: null,
},
hideFromForm: true,
}),
);
}

removeRestrictedPrefilled(index: number): void {
if (index < 0 || index >= this.prefilledValues.length) {
return;
}

const fieldToRemove = this.prefilledValues.at(index).value.field;

this.formConfig.fieldGroups.forEach((group) => {
group.fields = group.fields.filter((field: any) => {
if (typeof field === "object") {
return field.id !== fieldToRemove;
}
return field !== fieldToRemove;
});
});

this.prefilledValues.removeAt(index);

this.formControl.markAsDirty();
}

private updateFieldGroups(value): void {
if (!value?.prefilledvalue) return;

const fieldGroups = this.formConfig.fieldGroups || [];

if (fieldGroups.length === 0) {
fieldGroups.push({ fields: [] });
}

value.prefilledvalue.forEach((prefilledValue) => {
const fieldId = prefilledValue.field;
const defaultValue = prefilledValue.defaultValue;

if (!fieldId) {
return;
}
this.updateFieldInGroup(
fieldGroups[0].fields as unknown as FormFieldConfig[],
fieldId,
defaultValue,
);
});

setTimeout(() => this.formControl.setValue(fieldGroups));
this.formControl.markAsDirty();
}

private updateFieldInGroup(
fields: (string | FormFieldConfig)[],
fieldId: string,
defaultValue: DefaultValueConfig | null,
): void {
const updatedValue: FormFieldConfig = defaultValue
? { id: fieldId, defaultValue, hideFromForm: true }
: { id: fieldId, hideFromForm: true };

const fieldIndex = fields.findIndex((field) =>
typeof field === "string" ? field === fieldId : field.id === fieldId,
);

if (fieldIndex !== -1) {
if (typeof fields[fieldIndex] === "object") {
const existingField = fields[fieldIndex] as FormFieldConfig;
existingField.defaultValue = defaultValue;
existingField.hideFromForm = true;
}
} else {
fields.push(updatedValue);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ export class EditPublicFormColumnsComponent
this.formConfig = {
fieldGroups: publicFormConfig.columns,
};
this.formControl.valueChanges.subscribe(
(v) => (this.formConfig = { fieldGroups: v }),
);
}

this.originalEntitySchemaFields = JSON.parse(
Expand Down
6 changes: 6 additions & 0 deletions src/app/features/public-form/public-form-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,10 @@ export class PublicFormConfig extends Entity {

/** @deprecated use ColumnConfig directly in the columns array instead */
@DatabaseField() prefilled: { [key in string]: any };

@DatabaseField({
label: $localize`:PublicFormConfig:Restricted Prefilled`,
isArray: true,
})
restrictedPrefilled: FieldGroup[];
}
Loading
Loading