edit
This commit is contained in:
@@ -0,0 +1,39 @@
|
|||||||
|
.button1::after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button1:hover::after {
|
||||||
|
content: "ADD ROWS";
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
background-color: #dddddd;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section p {
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clr-input {
|
||||||
|
color: #212529;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
padding: 0.75rem 0.75rem;
|
||||||
|
background-color: rgb(255, 255, 255);
|
||||||
|
margin-top: 3px;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px 5px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
}/*# sourceMappingURL=adddynamicform.component.css.map */
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["adddynamicform.component.scss","adddynamicform.component.css"],"names":[],"mappings":"AAEA;EACE,aAAA;ACDF;;ADGA;EACE,mBAAA;ACAF;;ADGA;EACE,yBAVS;EAWT,YAAA;ACAF;;ADGA;EAEE,aAAA;EACA,eAAA;ACDF;;ADIA;EACE,cAAA;EACA,yBAAA;EACA,sBAAA;EACA,wBAAA;EACA,oCAAA;EACA,eAAA;EACA,WAAA;EACA,mBAAA;ACDF;;ADGA;EACE,WAAA;EACA,gBAAA;EACA,sBAAA;EACA,kBAAA;ACAF;;ADEA;EACE,kBAAA;ACCF","file":"adddynamicform.component.css"}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
<h4 style="font-weight: 300;display: inline;"><b>DYNAMIC FORM SETUP</b></h4>
|
||||||
|
<span class="label label-light-blue" style="display: inline;margin-left: 30px;">Add Mode</span><br>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<!-- Header form start -->
|
||||||
|
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()">
|
||||||
|
<div class="clr-row">
|
||||||
|
<div class="clr-col-md-4 clr-col-sm-12">
|
||||||
|
<label for="form_name">Form Name</label>
|
||||||
|
<input id="form_name" type="text" formControlName="form_name" placeholder="Enter Form Name"
|
||||||
|
class="clr-input">
|
||||||
|
<div *ngFor="let error of fieldErrors">
|
||||||
|
<span *ngIf="error.field === 'form_name'" class="text text-danger">{{ error.message }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="clr-col-md-4 clr-col-sm-12">
|
||||||
|
<label for="form_desc">Form Description</label>
|
||||||
|
<input id="form_desc" type="text" formControlName="form_desc" placeholder="Enter Form Description"
|
||||||
|
class="clr-input">
|
||||||
|
<div *ngFor="let error of fieldErrors">
|
||||||
|
<span *ngIf="error.field === 'form_desc'" class="text text-danger">{{ error.message }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="clr-col-md-4 clr-col-sm-12">
|
||||||
|
<label for="related_to">Related To</label>
|
||||||
|
<select id="related_to" formControlName="related_to" selected="null" class="clr-dropdown">
|
||||||
|
<option value="null">Choose Type</option>
|
||||||
|
<option *ngFor="let type of related_to" [ngValue]="type">{{ type }}</option>
|
||||||
|
</select>
|
||||||
|
<div *ngFor="let error of fieldErrors">
|
||||||
|
<span *ngIf="error.field === 'related_to'" class="text text-danger">{{ error.message }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="clr-col-md-4 clr-col-sm-12">
|
||||||
|
<label for="page_event">Page Event</label>
|
||||||
|
<select id="page_event" formControlName="page_event" selected="null" class="clr-dropdown">
|
||||||
|
<option value="null">Choose Type</option>
|
||||||
|
<option *ngFor="let type of page_event" [ngValue]="type">{{ type }}</option>
|
||||||
|
</select>
|
||||||
|
<div *ngFor="let error of fieldErrors">
|
||||||
|
<span *ngIf="error.field === 'page_event'" class="text text-danger">{{ error.message }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="clr-col-md-4 clr-col-sm-12">
|
||||||
|
<label for="button_caption">Button Name</label>
|
||||||
|
<input id="button_caption" type="text" formControlName="button_caption" placeholder="Enter Button Name"
|
||||||
|
class="clr-input">
|
||||||
|
<div *ngFor="let error of fieldErrors">
|
||||||
|
<span *ngIf="error.field === 'button_caption'" class="text text-danger">{{ error.message }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> <!-- row end -->
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<p> Component Details </p>
|
||||||
|
<hr>
|
||||||
|
<!-----H-L LINE part(FormArray For Multiple Lines)---->
|
||||||
|
<div class="clr-row">
|
||||||
|
<div class="clr-col-lg-12">
|
||||||
|
<table class="table" style="width:100%;" formArrayName="components">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="left" style="width:125px;">Label</th>
|
||||||
|
<th class="left" style="width:125px;">Type</th>
|
||||||
|
<th class="left" style="width:125px;">Mapping</th>
|
||||||
|
<th class="right" style="width:125px;">Readonly</th>
|
||||||
|
<th class="right" style="width:125px;">Drop Values</th>
|
||||||
|
<!-- <th *ngIf="controls.length > 1" class="right" style="width:125px;">{{ controls.length > 1 ? 'Actions' : '' }}Actions</th> -->
|
||||||
|
<th class="right" style="width:125px;">{{ controls.length > 1 ? 'Actions' : '' }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let item of controls; let i=index" [formGroupName]="i">
|
||||||
|
<td class="left">
|
||||||
|
<input type="text" [attr.id]="'label' + i" formControlName="label"
|
||||||
|
placeholder="Enter Label" style="width:180px" class="clr-input">
|
||||||
|
</td>
|
||||||
|
<td class="left">
|
||||||
|
<select colspan="2" formControlName="type" selected="null" style="width:180px"
|
||||||
|
class="clr-dropdown">
|
||||||
|
<option value="null">Choose Field Type</option>
|
||||||
|
<option *ngFor="let type of field_type" [ngValue]="type">{{ type }}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="left">
|
||||||
|
<select colspan="2" [attr.id]="'mapping' + i" selected="null" formControlName="mapping"
|
||||||
|
style="width:180px" class="clr-dropdown">
|
||||||
|
<option value="null">Choose Mapping</option>
|
||||||
|
<option *ngFor="let mapping of getFilteredMappings(i)" [ngValue]="mapping.value">{{
|
||||||
|
mapping.label
|
||||||
|
}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="center">
|
||||||
|
<div class="toggle-switch">
|
||||||
|
<input [attr.id]="'readonly' + i" type="checkbox" formControlName="readonly" />
|
||||||
|
<label [for]="'readonly' + i"></label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="left">
|
||||||
|
<ng-container *ngIf="controls[i].get('type').value === 'dropdown'">
|
||||||
|
<div style="display:flex; flex-wrap:wrap; gap:5px; margin-bottom:5px;">
|
||||||
|
<span *ngFor="let val of getDropValuesArray(i)" class="label label-blue">
|
||||||
|
{{ val }}
|
||||||
|
<clr-icon shape="times" (click)="removeDropValue(i, val)"
|
||||||
|
style="cursor:pointer; margin-left:5px;"></clr-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input type="text" placeholder="Type & Enter"
|
||||||
|
(keydown.enter)="$event.preventDefault(); addDropValue(i, $event)"
|
||||||
|
style="width:180px" class="clr-input">
|
||||||
|
<!-- Hidden input to bind formControl -->
|
||||||
|
<input type="text" [attr.id]="'drop_values' + i" formControlName="drop_values"
|
||||||
|
hidden>
|
||||||
|
</ng-container>
|
||||||
|
</td>
|
||||||
|
<td style="width:40px;">
|
||||||
|
<a *ngIf="controls.length > 1" (click)="onRemoveLines(i)">
|
||||||
|
<clr-icon shape="trash" class="is-error"></clr-icon>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
<button type="button" class="btn btn-primary button1" (click)="onAddLines()">
|
||||||
|
<clr-icon shape="plus"></clr-icon>
|
||||||
|
</button>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="center">
|
||||||
|
<button type="submit" class="btn btn-primary" [disabled]="!entryForm.valid">SUBMIT</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
$bg-color: #dddddd;
|
||||||
|
|
||||||
|
.button1::after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
.button1:hover::after {
|
||||||
|
content: "ADD ROWS";
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
background-color: $bg-color;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section p {
|
||||||
|
//color: white;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clr-input {
|
||||||
|
color: #212529;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
padding: 0.75rem 0.75rem;
|
||||||
|
background-color:rgb(255, 255, 255);
|
||||||
|
margin-top: 3px;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
select{
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px 5px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AdddynamicformComponent } from './adddynamicform.component';
|
||||||
|
|
||||||
|
describe('AdddynamicformComponent', () => {
|
||||||
|
let component: AdddynamicformComponent;
|
||||||
|
let fixture: ComponentFixture<AdddynamicformComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ AdddynamicformComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AdddynamicformComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormArray, FormBuilder, FormGroup } from '@angular/forms';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { ValidationError } from 'src/app/models/fnd/ValidationError';
|
||||||
|
import { DynamicformService } from 'src/app/services/fnd/dynamicform.service';
|
||||||
|
import { Mapping } from "src/app/models/fnd/Mapping";
|
||||||
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-adddynamicform',
|
||||||
|
templateUrl: './adddynamicform.component.html',
|
||||||
|
styleUrls: ['./adddynamicform.component.scss']
|
||||||
|
})
|
||||||
|
export class AdddynamicformComponent implements OnInit {
|
||||||
|
public entryForm: FormGroup;
|
||||||
|
submitted = false;
|
||||||
|
basic: boolean = false;
|
||||||
|
fieldErrors: ValidationError[] = [];
|
||||||
|
related_to = ['Menu', 'Related To'];
|
||||||
|
page_event = ['OnClick', 'OnBlur'];
|
||||||
|
field_type = ['text', 'dropdown', 'date', 'checkbox', 'textarea', 'togglebutton'];
|
||||||
|
// mappings: Mapping[];
|
||||||
|
mappings = [
|
||||||
|
{ label: 'textfield1', value: 'comp1' },
|
||||||
|
{ label: 'textfield2', value: 'comp2' },
|
||||||
|
{ label: 'textfield3', value: 'comp3' },
|
||||||
|
{ label: 'textfield4', value: 'comp4' },
|
||||||
|
{ label: 'textfield5', value: 'comp5' },
|
||||||
|
{ label: 'textfield6', value: 'comp6' },
|
||||||
|
{ label: 'textfield7', value: 'comp7' },
|
||||||
|
{ label: 'textfield8', value: 'comp8' },
|
||||||
|
{ label: 'textfield9', value: 'comp9' },
|
||||||
|
{ label: 'textfield10', value: 'comp10' },
|
||||||
|
{ label: 'textfield11', value: 'comp11' },
|
||||||
|
{ label: 'textfield12', value: 'comp12' },
|
||||||
|
{ label: 'textfield13', value: 'comp13' },
|
||||||
|
{ label: 'textfield14', value: 'comp14' },
|
||||||
|
{ label: 'textfield15', value: 'comp15' },
|
||||||
|
{ label: 'textfield16', value: 'comp16' },
|
||||||
|
{ label: 'textfield17', value: 'comp17' },
|
||||||
|
{ label: 'textfield18', value: 'comp18' },
|
||||||
|
{ label: 'textfield19', value: 'comp19' },
|
||||||
|
{ label: 'textfield20', value: 'comp20' },
|
||||||
|
{ label: 'textfield21', value: 'comp21' },
|
||||||
|
{ label: 'textfield22', value: 'comp22' },
|
||||||
|
{ label: 'textfield23', value: 'comp23' },
|
||||||
|
{ label: 'textfield24', value: 'comp24' },
|
||||||
|
{ label: 'textfield25', value: 'comp25' },
|
||||||
|
{ label: 'textfield26', value: 'comp26' },
|
||||||
|
{ label: 'longtext1', value: 'comp_l27' },
|
||||||
|
{ label: 'longtext2', value: 'comp_l28' },
|
||||||
|
{ label: 'longtext3', value: 'comp_l29' },
|
||||||
|
{ label: 'longtext4', value: 'comp_l30' }
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
constructor(private _fb: FormBuilder,
|
||||||
|
private router: Router,
|
||||||
|
private toastr: ToastrService,
|
||||||
|
private dynamicservice: DynamicformService,
|
||||||
|
private httpService: HttpClient,
|
||||||
|
private route: ActivatedRoute,) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
// this.getMapings();
|
||||||
|
console.log(this.mappings);
|
||||||
|
this.entryForm = this._fb.group({
|
||||||
|
form_name: [null],
|
||||||
|
form_desc: [null],
|
||||||
|
related_to: [null],
|
||||||
|
page_event: [null],
|
||||||
|
button_caption: [null],
|
||||||
|
components: this._fb.array([this.initLinesForm()]),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// getMapings() {
|
||||||
|
// this.httpService
|
||||||
|
// .get<Mapping[]>('./assets/json/form-setup-mapping.json')
|
||||||
|
// .subscribe(data => {
|
||||||
|
// console.log(data);
|
||||||
|
// this.mappings = data;
|
||||||
|
// }, err => console.log(err)
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
initLinesForm() {
|
||||||
|
return this._fb.group({
|
||||||
|
label: [null],
|
||||||
|
type: [null],
|
||||||
|
mapping: [null],
|
||||||
|
mandatory: [null],
|
||||||
|
readonly: [null],
|
||||||
|
drop_values: [null],
|
||||||
|
sp: [null],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get controls() {
|
||||||
|
return (this.entryForm.get("components") as FormArray).controls;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilteredMappings(index: number) {
|
||||||
|
const components = this.entryForm.get('components') as FormArray;
|
||||||
|
// Get all selected values from other rows
|
||||||
|
const selectedValues = components.controls
|
||||||
|
.map((control, i) => {
|
||||||
|
if (i === index) return null; // Don't block current row's value
|
||||||
|
return control.get('mapping').value;
|
||||||
|
})
|
||||||
|
.filter(val => val !== null && val !== 'null' && val !== '');
|
||||||
|
|
||||||
|
// Return mappings that remain
|
||||||
|
return this.mappings.filter(m => !selectedValues.includes(m.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag Input Helpers
|
||||||
|
getDropValuesArray(index: number): string[] {
|
||||||
|
const control = (this.entryForm.get('components') as FormArray).at(index).get('drop_values');
|
||||||
|
const val = control.value;
|
||||||
|
if (!val) return [];
|
||||||
|
return val.split(',').map(s => s.trim()).filter(s => s.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
addDropValue(index: number, event: any) {
|
||||||
|
const value = event.target.value;
|
||||||
|
if (!value) return;
|
||||||
|
|
||||||
|
const control = (this.entryForm.get('components') as FormArray).at(index).get('drop_values');
|
||||||
|
const currentArr = this.getDropValuesArray(index);
|
||||||
|
|
||||||
|
if (!currentArr.includes(value.trim())) {
|
||||||
|
currentArr.push(value.trim());
|
||||||
|
control.setValue(currentArr.join(','));
|
||||||
|
}
|
||||||
|
event.target.value = ''; // Clear input
|
||||||
|
}
|
||||||
|
|
||||||
|
removeDropValue(index: number, valueToRemove: string) {
|
||||||
|
const control = (this.entryForm.get('components') as FormArray).at(index).get('drop_values');
|
||||||
|
let currentArr = this.getDropValuesArray(index);
|
||||||
|
currentArr = currentArr.filter(v => v !== valueToRemove);
|
||||||
|
control.setValue(currentArr.join(','));
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddLines() {
|
||||||
|
(<FormArray>this.entryForm.get("components")).push(this.initLinesForm());
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemoveLines(index: number) {
|
||||||
|
(<FormArray>this.entryForm.get("components")).removeAt(index);
|
||||||
|
}
|
||||||
|
onSubmit() {
|
||||||
|
console.log(this.entryForm.value);
|
||||||
|
this.dynamicservice.create(this.entryForm.value).subscribe(
|
||||||
|
(data) => {
|
||||||
|
console.log(data);
|
||||||
|
this.router.navigate(["../all"], { relativeTo: this.route });
|
||||||
|
if (data) {
|
||||||
|
this.toastr.success('Added successfully');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.log(error);
|
||||||
|
const objectArray = Object.entries(error.error.fieldErrors);
|
||||||
|
objectArray.forEach(([k, v]) => {
|
||||||
|
console.log(k);
|
||||||
|
console.log(v);
|
||||||
|
this.fieldErrors.push({ field: k, message: v });
|
||||||
|
});
|
||||||
|
console.log(this.fieldErrors); // this will come from backend
|
||||||
|
if (error) {
|
||||||
|
this.toastr.error('Not Added Data Getting Some Error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
.td-title {
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: rgba(63, 122, 231, 0.863);
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: rgb(170, 169, 169);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.td-content {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete, .heading {
|
||||||
|
text-align: center;
|
||||||
|
color: red;
|
||||||
|
}/*# sourceMappingURL=alldynamicform.component.css.map */
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["alldynamicform.component.scss","alldynamicform.component.css"],"names":[],"mappings":"AAAA;EACE,kBAAA;EACF,YAAA;EACE,iBAAA;EACA,2CAAA;ACCF;;ADEA;EACE,oCAAA;EACA,iBAAA;ACCF;;ADEA;EACE,gBAAA;ACCF;;ADCA;EACE,kBAAA;EACA,UAAA;ACEF","file":"alldynamicform.component.css"}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
<ol class="breadcrumb breadcrumb-arrow font-trirong">
|
||||||
|
<li><a href="javascript://" [routerLink]="['/']"><clr-icon shape="home"></clr-icon></a></li>
|
||||||
|
<li><a href="javascript://"> <clr-icon shape="crown"></clr-icon>Super Admin</a></li>
|
||||||
|
<li><a href="javascript://">Dynamic Form</a></li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<div class="dg-wrapper">
|
||||||
|
<div class="clr-row">
|
||||||
|
<div class="clr-col-8">
|
||||||
|
<h3>Dynamic Form </h3>
|
||||||
|
</div>
|
||||||
|
<div class="clr-col-4" style="text-align: right;">
|
||||||
|
|
||||||
|
|
||||||
|
<button id="add" class="btn btn-primary" (click)="goToAdd()" >
|
||||||
|
<clr-icon shape="plus"></clr-icon>ADD
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<clr-datagrid [clrDgLoading]="loading" [(clrDgSelected)]="selected">
|
||||||
|
|
||||||
|
<clr-dg-placeholder><ng-template #loadingSpinner><clr-spinner>Loading ... </clr-spinner></ng-template>
|
||||||
|
<div *ngIf="error;else loadingSpinner">{{error}}</div></clr-dg-placeholder>
|
||||||
|
|
||||||
|
<clr-dg-column [clrDgField]="''" style="max-width: 40px;"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
</ng-container></clr-dg-column>
|
||||||
|
|
||||||
|
<clr-dg-column [clrDgField]="''"><ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Go To
|
||||||
|
</ng-container></clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'formname'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Form Name
|
||||||
|
</ng-container></clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'jobName'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Form Description
|
||||||
|
</ng-container></clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="' jobGroup'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Related To
|
||||||
|
</ng-container> </clr-dg-column >
|
||||||
|
<clr-dg-column [clrDgField]="'jobStatus'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Page Event </ng-container></clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'jobClass'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Button Caption
|
||||||
|
</ng-container></clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'description'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Go To Form
|
||||||
|
</ng-container></clr-dg-column>
|
||||||
|
<clr-dg-column> <ng-container *clrDgHideableColumn="{hidden: false}"> <clr-icon shape="bars"></clr-icon>
|
||||||
|
Action
|
||||||
|
</ng-container></clr-dg-column>
|
||||||
|
|
||||||
|
<clr-dg-row *clrDgItems="let user of data" [clrDgItem]="user" >
|
||||||
|
<clr-dg-cell style="max-width: 40px;">
|
||||||
|
<span style="cursor: pointer;"><clr-icon shape="edit" (click)="goToEdit(user.form_id)" class="red is-error" style="color:red;"></clr-icon></span>
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell><span class="label label-light-blue" style="display: inline;margin-left: 10px; cursor: pointer;" (click)="buildDynamicForm(user.form_id)">Build</span></clr-dg-cell>
|
||||||
|
|
||||||
|
<clr-dg-cell>{{user.form_name}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.form_desc}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.related_to}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.page_event}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.button_caption}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell> <a (click)="goToForm(user.form_id)" attr.data-itemid="{{user.form_id}}">OPEN FORM</a></clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<span style="cursor: pointer;padding: 10px; "><clr-icon shape="trash" (click)="onDelete(user)" class="red is-error" style="color: red;"></clr-icon></span>
|
||||||
|
<clr-signpost>
|
||||||
|
<span style="cursor: pointer;" clrSignpostTrigger><clr-icon shape="help" class="success" style="color: rgb(0, 130, 236);"></clr-icon></span>
|
||||||
|
<clr-signpost-content [clrPosition]="'left-middle'" *clrIfOpen>
|
||||||
|
<h5 style="margin-top: 0">Who Column</h5>
|
||||||
|
<div>Account ID: <code class="clr-code">{{user.accountId}}</code></div>
|
||||||
|
<div>Created At: <code class="clr-code">{{user.createdAt| date}}</code></div>
|
||||||
|
<div>Created By: <code class="clr-code">{{user.createdBy}}</code></div>
|
||||||
|
<div>Updated At: <code class="clr-code">{{user.updatedAt | date}}</code></div>
|
||||||
|
<div>Updated By: <code class="clr-code">{{user.updatedBy}}</code></div>
|
||||||
|
</clr-signpost-content>
|
||||||
|
</clr-signpost>
|
||||||
|
</clr-dg-cell>
|
||||||
|
|
||||||
|
<!-- <clr-dg-action-overflow>
|
||||||
|
<button class="action-item" (click)="buildDynamicForm(user.form_id)">Build <clr-icon shape="pop-out" class="is-error"></clr-icon> </button>
|
||||||
|
<button class="action-item" (click)="goToEdit(user.form_id)">Edit<clr-icon shape="edit" class="is-error"></clr-icon></button>
|
||||||
|
<button class="action-item" (click)="onDelete(user)">Delete<clr-icon shape="trash" class="is-error"></clr-icon></button>
|
||||||
|
</clr-dg-action-overflow> -->
|
||||||
|
<clr-dg-row-detail *clrIfExpanded >
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<td class="td-title">Form Name:{{user.form_name}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="td-title">Form_desc:{{user.form_desc}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="td-title">Related_to:{{user.related_to}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="td-title">Page_event:{{user.page_event}}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
<table id="lines" class="table" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="left">Lable</th>
|
||||||
|
<th class="left" >Type</th>
|
||||||
|
<th class="left" >Mapping</th>
|
||||||
|
<th class="left" >ReadOnly</th>
|
||||||
|
<th class="left" >Drop Values</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let person of user.components">
|
||||||
|
<td class="left"> {{person.label}}</td>
|
||||||
|
<td class="left">{{person.type}}</td>
|
||||||
|
<td class="left">{{person.mapping}} </td>
|
||||||
|
<td class="left">{{person.readonly}}</td>
|
||||||
|
<td class="left"> {{person.drop_values}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</table>
|
||||||
|
</clr-dg-row-detail>
|
||||||
|
</clr-dg-row>
|
||||||
|
<clr-dg-footer>
|
||||||
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Users per page</clr-dg-page-size>
|
||||||
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
|
of {{pagination.totalItems}} users
|
||||||
|
</clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<clr-modal [(clrModalOpen)]="modaldelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
||||||
|
|
||||||
|
<div class="modal-body" *ngIf="rowSelected.form_id">
|
||||||
|
<h1 class="delete">Are You Sure Want to delete?</h1>
|
||||||
|
<h2 class="heading">{{rowSelected.form_id}}</h2>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline" (click)="modaldelete = false">Cancel</button>
|
||||||
|
<button type="button" (click)="delete(rowSelected.form_id)" class="btn btn-primary" >Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
.td-title {
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: rgba(63, 122, 231, 0.863);
|
||||||
|
//color: rgb(24, 13, 13);
|
||||||
|
}
|
||||||
|
th{
|
||||||
|
background-color:rgb(170, 169, 169);
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
}
|
||||||
|
.td-content{
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.delete,.heading{
|
||||||
|
text-align: center;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AlldynamicformComponent } from './alldynamicform.component';
|
||||||
|
|
||||||
|
describe('AlldynamicformComponent', () => {
|
||||||
|
let component: AlldynamicformComponent;
|
||||||
|
let fixture: ComponentFixture<AlldynamicformComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ AlldynamicformComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AlldynamicformComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
import { DynamicformService } from '../../../../../services/fnd/dynamicform.service'
|
||||||
|
import { MenumaintanceService } from 'src/app/services/admin/menumaintance.service';
|
||||||
|
import { Rn_Forms_Setup } from 'src/app/models/fnd/Rn_Forms_Setup';
|
||||||
|
@Component({
|
||||||
|
selector: 'app-alldynamicform',
|
||||||
|
templateUrl: './alldynamicform.component.html',
|
||||||
|
styleUrls: ['./alldynamicform.component.scss']
|
||||||
|
})
|
||||||
|
export class AlldynamicformComponent implements OnInit {
|
||||||
|
rowSelected: any = {};
|
||||||
|
modaldelete = false;
|
||||||
|
loading = false;
|
||||||
|
data;
|
||||||
|
error;
|
||||||
|
selected: any[] = [];
|
||||||
|
constructor(private dynamicservice: DynamicformService,
|
||||||
|
private router: Router,
|
||||||
|
private toastr: ToastrService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private menumaintanceService: MenumaintanceService,
|
||||||
|
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.getall();
|
||||||
|
}
|
||||||
|
getall() {
|
||||||
|
this.dynamicservice.getAll().subscribe((data) => {
|
||||||
|
this.data = data.items;
|
||||||
|
this.data = [...this.data].reverse();
|
||||||
|
console.log('dynmic data ', data);
|
||||||
|
if (data.length == 0) {
|
||||||
|
this.error = "No data Available plz add if Required";
|
||||||
|
console.log(this.error)
|
||||||
|
}
|
||||||
|
}, (error) => {
|
||||||
|
console.log(error);
|
||||||
|
if (error) {
|
||||||
|
this.error = "Server Error";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
goToAdd() {
|
||||||
|
this.router.navigate(["../add"], { relativeTo: this.route });
|
||||||
|
}
|
||||||
|
goToEdit(id: number) {
|
||||||
|
console.log("goToEdit() id = " + id);
|
||||||
|
this.router.navigate(["../edit/" + id], { relativeTo: this.route });
|
||||||
|
}
|
||||||
|
goToForm(id: number) {
|
||||||
|
this.router.navigate(['/cns-portal/dynamicform1'], { queryParams: { form_id: id } });
|
||||||
|
|
||||||
|
}
|
||||||
|
form_id;
|
||||||
|
alertType: string;
|
||||||
|
alertMessage: string = "";
|
||||||
|
alert = [
|
||||||
|
{ type: "success", message: "Build Successfully" },
|
||||||
|
{ type: "danger", message: "Some error Happens" },
|
||||||
|
];
|
||||||
|
buildDynamicForm(id) {
|
||||||
|
this.form_id = id;
|
||||||
|
console.log("buildDynamicForm() Form_id = " + this.form_id);
|
||||||
|
if (this.form_id === null) {
|
||||||
|
this.alertType = this.alert[1].type;
|
||||||
|
this.alertMessage = "form_code is null";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch form definition to check relation
|
||||||
|
this.dynamicservice.getById(id).subscribe(
|
||||||
|
(form: Rn_Forms_Setup) => {
|
||||||
|
if (form && form.related_to === 'Menu') {
|
||||||
|
this.createMenuForForm(form);
|
||||||
|
} else {
|
||||||
|
// Standard behavior for non-Menu forms
|
||||||
|
console.log("Form construction handled by frontend viewer.");
|
||||||
|
this.alertType = this.alert[0].type;
|
||||||
|
this.alertMessage = this.alert[0].message;
|
||||||
|
this.toastr.success("Form Ready for Viewing (Frontend Render)", "Success");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
console.error(err);
|
||||||
|
this.toastr.error("Failed to fetch form details");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
createMenuForForm(form: Rn_Forms_Setup) {
|
||||||
|
// Construct Payload for Menu Creation
|
||||||
|
// Requirement: menuId = 12832
|
||||||
|
// Link: dynamicform1?form_id={id}
|
||||||
|
|
||||||
|
const menuPayload = {
|
||||||
|
menuId: 12832,
|
||||||
|
menuItemDesc: form.form_name,
|
||||||
|
itemSeq: 1, // Default sequence
|
||||||
|
moduleName: form.form_name,
|
||||||
|
status: 'Enable',
|
||||||
|
main_menu_action_name: `dynamicform1/${form.form_id}`
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check for duplicate menu
|
||||||
|
this.menumaintanceService.getLatestMenu(12832, menuPayload.moduleName, menuPayload.main_menu_action_name)
|
||||||
|
.subscribe(
|
||||||
|
(existingMenu) => {
|
||||||
|
if (existingMenu) {
|
||||||
|
console.log('Menu already exists:', existingMenu);
|
||||||
|
this.toastr.info('Menu Already Created');
|
||||||
|
} else {
|
||||||
|
// Not found, create it
|
||||||
|
this.proceedToCreateMenu(menuPayload);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
console.error('Error checking duplicate menu', err);
|
||||||
|
// Optional: Proceed if error is 404, but angular http might treat 404 as error
|
||||||
|
// If backend returns null for not found, we fall into if(existingMenu) else block.
|
||||||
|
// If backend throws error for not found, we handle here.
|
||||||
|
// Assuming backend returns empty body or null 200 OK if not found is ideal,
|
||||||
|
// but if it throws exception, we might want to proceed?
|
||||||
|
// Safe bet: if error, try to create or warn.
|
||||||
|
// Given msg "menu already created" requirement, let's assume valid check.
|
||||||
|
// Let's assume error means "Not Found" -> Create
|
||||||
|
this.proceedToCreateMenu(menuPayload);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
proceedToCreateMenu(menuPayload: any) {
|
||||||
|
console.log('Building Menu for Form:', menuPayload);
|
||||||
|
|
||||||
|
this.menumaintanceService.create1(menuPayload).subscribe(
|
||||||
|
(data) => {
|
||||||
|
console.log('Menu Item Created:', data);
|
||||||
|
this.toastr.success('Menu Item Created. Syncing...');
|
||||||
|
|
||||||
|
// 3. Sync
|
||||||
|
this.syncMenu(menuPayload);
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
console.error('Failed to create menu item', err);
|
||||||
|
this.toastr.error('Failed to create menu item');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
syncMenu(payload: any) {
|
||||||
|
const syncPayload = { ...payload };
|
||||||
|
if (syncPayload.subMenus && syncPayload.subMenus !== 0) {
|
||||||
|
syncPayload.subMenus = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.menumaintanceService.sink(12832, syncPayload).subscribe(
|
||||||
|
(data) => {
|
||||||
|
this.toastr.success('Menu Synced Successfully');
|
||||||
|
this.toastr.info('Form is now accessible via Menu');
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
console.error('Sync failed', err);
|
||||||
|
this.toastr.error('Menu Sync Failed');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDelete(row) {
|
||||||
|
this.rowSelected = row;
|
||||||
|
this.modaldelete = true;
|
||||||
|
}
|
||||||
|
delete(id) {
|
||||||
|
this.modaldelete = false;
|
||||||
|
console.log("in delete " + id);
|
||||||
|
this.dynamicservice.delete(id).subscribe((data) => {
|
||||||
|
console.log(data);
|
||||||
|
this.ngOnInit();
|
||||||
|
if (data.body) {
|
||||||
|
this.toastr.success('Deleted successfully');
|
||||||
|
}
|
||||||
|
}, (error) => {
|
||||||
|
console.log('Error in adding data...', +error);
|
||||||
|
if (error) {
|
||||||
|
this.toastr.error('Not Deleted Data Getting Some Error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
<div class="dg-wrapper" *ngIf="formSetup; else loadingTpl">
|
||||||
|
|
||||||
|
<!-- HEADER SECTION -->
|
||||||
|
<div class="clr-row" style="margin-bottom: 20px;">
|
||||||
|
<div class="clr-col-8">
|
||||||
|
<h3>{{ formSetup.form_name }} Form Data</h3>
|
||||||
|
</div>
|
||||||
|
<div class="clr-col-4" style="text-align: right;">
|
||||||
|
<button class="btn btn-primary" (click)="goToAdd()" *ngIf="viewMode === 'list'">
|
||||||
|
<clr-icon shape="plus"></clr-icon> ADD NEW
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline" (click)="cancel()" *ngIf="viewMode !== 'list'">
|
||||||
|
CANCEL
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- LIST VIEW: DATA GRID -->
|
||||||
|
<div *ngIf="viewMode === 'list'">
|
||||||
|
<clr-datagrid>
|
||||||
|
<clr-dg-column *ngFor="let col of gridColumns" [clrDgField]="col.field">
|
||||||
|
{{ col.header }}
|
||||||
|
</clr-dg-column>
|
||||||
|
|
||||||
|
<clr-dg-column>Actions</clr-dg-column>
|
||||||
|
|
||||||
|
<clr-dg-row *clrDgItems="let item of gridData" [clrDgItem]="item">
|
||||||
|
<clr-dg-cell *ngFor="let col of gridColumns">
|
||||||
|
{{ item[col.field] }}
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<button class="btn btn-sm btn-icon btn-primary" (click)="goToEdit(item)" title="Edit">
|
||||||
|
<clr-icon shape="pencil"></clr-icon>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-icon btn-danger" (click)="onDelete(item)" title="Delete">
|
||||||
|
<clr-icon shape="trash"></clr-icon>
|
||||||
|
</button>
|
||||||
|
</clr-dg-cell>
|
||||||
|
</clr-dg-row>
|
||||||
|
|
||||||
|
<clr-dg-footer>
|
||||||
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Items per page</clr-dg-page-size>
|
||||||
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
|
of {{pagination.totalItems}} items
|
||||||
|
</clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ADD/EDIT VIEW: ENTRY FORM -->
|
||||||
|
<div *ngIf="viewMode === 'add' || viewMode === 'edit'" class="entry-pg pad-16">
|
||||||
|
<h4><b>{{ viewMode === 'add' ? 'ENTRY FORM' : 'EDIT FORM' }}</b></h4>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<section class="form-block" style="margin-top:20px">
|
||||||
|
<form [formGroup]="dynamicForm" (ngSubmit)="onSubmit()">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let comp of formSetup.components">
|
||||||
|
<!-- TEXTFIELD -->
|
||||||
|
<ng-container *ngIf="isTextField(comp.type)">
|
||||||
|
<td style="width:150px;"><b>{{ comp.label }}:</b></td>
|
||||||
|
<td>
|
||||||
|
<input colspan="2" type="text" [formControlName]="comp.generatedControlName"
|
||||||
|
[placeholder]="comp.label" class="clr-input"
|
||||||
|
style="width: 100%; max-width: 300px;">
|
||||||
|
<div *ngIf="dynamicForm.get(comp.generatedControlName)?.invalid && (dynamicForm.get(comp.generatedControlName)?.dirty || dynamicForm.get(comp.generatedControlName)?.touched)"
|
||||||
|
class="text-danger">
|
||||||
|
{{ comp.label }} is required
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- LONGTEXT -->
|
||||||
|
<ng-container *ngIf="isTextArea(comp.type)">
|
||||||
|
<td style="width:150px;"><b>{{ comp.label }}:</b></td>
|
||||||
|
<td>
|
||||||
|
<textarea rows="4" cols="50" colspan="2" class="clr-textarea"
|
||||||
|
style="width: 100%; max-width: 400px;"
|
||||||
|
[formControlName]="comp.generatedControlName"></textarea>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- CHECKBOX -->
|
||||||
|
<ng-container *ngIf="isCheckbox(comp.type)">
|
||||||
|
<td style="width:150px;"><b>{{ comp.label }}</b></td>
|
||||||
|
<td>
|
||||||
|
<input colspan="2" type="checkbox" [formControlName]="comp.generatedControlName"
|
||||||
|
class="clr-checkbox">
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- AUTOCOMPLETE -->
|
||||||
|
<ng-container *ngIf="isAutocomplete(comp.type)">
|
||||||
|
<td style="width:150px;"><b>{{ comp.label }}</b></td>
|
||||||
|
<td>
|
||||||
|
<input colspan="2" type="text" [formControlName]="comp.generatedControlName"
|
||||||
|
autocomplete="on" class="clr-input" style="width: 100%; max-width: 300px;">
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- DROPDOWN -->
|
||||||
|
<ng-container *ngIf="isDropdown(comp.type)">
|
||||||
|
<td style="width:150px;"><b>{{ comp.label }}:</b></td>
|
||||||
|
<td>
|
||||||
|
<select colspan="2" [formControlName]="comp.generatedControlName" class="clr-select"
|
||||||
|
style="width: 100%; max-width: 300px;">
|
||||||
|
<option value="">Select {{ comp.label }}</option>
|
||||||
|
<option *ngFor="let opt of getDropdownOptions(comp.drop_values)" [value]="opt">
|
||||||
|
{{ opt }}</option>
|
||||||
|
</select>
|
||||||
|
<div *ngIf="dynamicForm.get(comp.generatedControlName)?.invalid && (dynamicForm.get(comp.generatedControlName)?.dirty || dynamicForm.get(comp.generatedControlName)?.touched)"
|
||||||
|
class="text-danger">
|
||||||
|
{{ comp.label }} is required
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- DATE -->
|
||||||
|
<ng-container *ngIf="isDate(comp.type)">
|
||||||
|
<td style="width:150px;"><b>{{ comp.label }}:</b></td>
|
||||||
|
<td>
|
||||||
|
<input colspan="2" type="date" [formControlName]="comp.generatedControlName"
|
||||||
|
class="clr-input" style="width: 100%; max-width: 300px;">
|
||||||
|
<div *ngIf="dynamicForm.get(comp.generatedControlName)?.invalid && (dynamicForm.get(comp.generatedControlName)?.dirty || dynamicForm.get(comp.generatedControlName)?.touched)"
|
||||||
|
class="text-danger">
|
||||||
|
{{ comp.label }} is required
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- TOGGLE -->
|
||||||
|
<ng-container *ngIf="isToggle(comp.type)">
|
||||||
|
<td style="width:150px;"><b>{{ comp.label }}:</b></td>
|
||||||
|
<td>
|
||||||
|
<div class="toggle-switch">
|
||||||
|
<input type="checkbox" [id]="comp.generatedControlName"
|
||||||
|
[formControlName]="comp.generatedControlName">
|
||||||
|
<label [for]="comp.generatedControlName"></label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
<div style="margin-top: 20px;">
|
||||||
|
<button type="submit" class="btn btn-primary" [disabled]="!dynamicForm.valid">
|
||||||
|
{{ viewMode === 'add' ? (formSetup.button_caption || 'SUBMIT') : 'UPDATE' }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-outline" (click)="cancel()">CANCEL</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-template #loadingTpl>
|
||||||
|
<div style="text-align: center; padding: 50px;" *ngIf="loading">
|
||||||
|
<span class="spinner"></span> Loading form configuration...
|
||||||
|
</div>
|
||||||
|
<div *ngIf="error" class="alert alert-danger" style="margin-top: 20px;">{{ error }}</div>
|
||||||
|
</ng-template>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
.entry-pg {
|
||||||
|
background: #fff;
|
||||||
|
padding: 16px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.form-block {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
.text-danger {
|
||||||
|
color: red;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
.toggle-switch { display: inline-block; position: relative; width: 44px; height: 24px; } .toggle-switch input { display: none; } .toggle-switch label { display: block; overflow: hidden; cursor: pointer; border: 0 solid #bbb; border-radius: 20px; margin: 0; } .toggle-switch label:before { content: ''; display: block; width: 100%; height: 100%; background-color: #f1f1f1; border: 1px solid #bbb; border-radius: 20px; transition: background-color 0.2s; } .toggle-switch label:after { content: ''; display: block; width: 18px; height: 18px; background: #fff; position: absolute; top: 3px; left: 3px; border-radius: 100%; box-shadow: 0 1px 3px rgba(0,0,0,0.4); transition: margin 0.2s; } .toggle-switch input:checked + label:before { background-color: #007cbb; border-color: #007cbb; } .toggle-switch input:checked + label:after { margin-left: 20px; }
|
||||||
@@ -0,0 +1,284 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { DynamicformService } from '../../../../../services/fnd/dynamicform.service';
|
||||||
|
import { Rn_Forms_Setup } from 'src/app/models/fnd/Rn_Forms_Setup';
|
||||||
|
import { Rn_Forms_Component_Setup } from 'src/app/models/fnd/Rn_Forms_Component_Setup';
|
||||||
|
|
||||||
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-dynamic-form-viewer',
|
||||||
|
templateUrl: './dynamic-form-viewer.component.html',
|
||||||
|
styleUrls: ['./dynamic-form-viewer.component.scss']
|
||||||
|
})
|
||||||
|
export class DynamicFormViewerComponent implements OnInit {
|
||||||
|
formId: number;
|
||||||
|
formSetup: Rn_Forms_Setup;
|
||||||
|
dynamicForm: FormGroup;
|
||||||
|
loading = false;
|
||||||
|
error = '';
|
||||||
|
|
||||||
|
viewMode: 'list' | 'add' | 'edit' = 'list';
|
||||||
|
gridColumns: any[] = [];
|
||||||
|
gridData: any[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
private dynamicFormService: DynamicformService,
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private toastr: ToastrService
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Check Query Params first
|
||||||
|
this.route.queryParams.subscribe(params => {
|
||||||
|
const id = params['form_id'];
|
||||||
|
if (id) {
|
||||||
|
this.formId = Number(id);
|
||||||
|
this.loadFormSetup(this.formId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check Route Params if Query Params didn't trigger
|
||||||
|
this.route.params.subscribe(params => {
|
||||||
|
const id = params['form_id'];
|
||||||
|
if (id && !this.formId) {
|
||||||
|
this.formId = Number(id);
|
||||||
|
this.loadFormSetup(this.formId);
|
||||||
|
} else if (!id && !this.formId) {
|
||||||
|
this.error = "Form ID is missing.";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadFormSetup(id: number) {
|
||||||
|
this.loading = true;
|
||||||
|
this.dynamicFormService.getById(id).subscribe(
|
||||||
|
(data: Rn_Forms_Setup) => {
|
||||||
|
this.formSetup = data;
|
||||||
|
if (this.formSetup && this.formSetup.components) {
|
||||||
|
this.buildForm(this.formSetup.components);
|
||||||
|
this.setupGridColumns(this.formSetup.components);
|
||||||
|
this.loadData();
|
||||||
|
} else {
|
||||||
|
this.error = "Form configuration is empty or invalid.";
|
||||||
|
}
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
console.error(err);
|
||||||
|
this.error = "Failed to load form setup from server.";
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setupGridColumns(components: Rn_Forms_Component_Setup[]) {
|
||||||
|
this.gridColumns = [];
|
||||||
|
// Always add ID column at the start (hidden or visible)
|
||||||
|
// this.gridColumns.push({ field: 'id', header: 'ID', type: 'number' });
|
||||||
|
|
||||||
|
let loopCount = 0;
|
||||||
|
components.forEach((comp) => {
|
||||||
|
loopCount++;
|
||||||
|
const type = comp.type ? comp.type.toLowerCase() : '';
|
||||||
|
|
||||||
|
// Exclude heavy text fields from grid
|
||||||
|
if (!this.isTextArea(type)) {
|
||||||
|
// Ensure we use the exact same control name as in the form group
|
||||||
|
// The buildForm logic uses:
|
||||||
|
let controlName = `comp${loopCount}`;
|
||||||
|
if (type === 'textarea' || type === 'longtext' || type === 'long text') {
|
||||||
|
controlName = `comp_l${25 + loopCount}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.gridColumns.push({
|
||||||
|
field: controlName, // Use the actual key bound to the data
|
||||||
|
header: comp.label,
|
||||||
|
type: type
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getStorageKey(): string {
|
||||||
|
return `dynamic_form_data_${this.formId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadData() {
|
||||||
|
// Use real API
|
||||||
|
this.dynamicFormService.getTransactionsByFormId(this.formId).subscribe(
|
||||||
|
(data: any[]) => {
|
||||||
|
this.gridData = data || [];
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
console.error(err);
|
||||||
|
this.toastr.error('Failed to load data');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// saveData() {
|
||||||
|
// localStorage.setItem(this.getStorageKey(), JSON.stringify(this.gridData));
|
||||||
|
// } // REMOVED
|
||||||
|
|
||||||
|
buildForm(components: Rn_Forms_Component_Setup[]) {
|
||||||
|
const group: any = {};
|
||||||
|
let loopCount = 0;
|
||||||
|
|
||||||
|
components.forEach((comp) => {
|
||||||
|
loopCount++;
|
||||||
|
const i = loopCount;
|
||||||
|
|
||||||
|
let controlName = `comp${i}`;
|
||||||
|
const type = comp.type ? comp.type.toLowerCase() : '';
|
||||||
|
|
||||||
|
// Match the logic in Java backend for field naming
|
||||||
|
if (type === 'textarea' || type === 'longtext' || type === 'long text') {
|
||||||
|
controlName = `comp_l${25 + i}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const validators = [];
|
||||||
|
if (comp.mandatory === 'true' || comp.mandatory === 'True' || comp.mandatory === '1') {
|
||||||
|
validators.push(Validators.required);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDisabled = (comp.readonly === 'true' || comp.readonly === 'True' || comp.readonly === '1');
|
||||||
|
|
||||||
|
let defaultValue = '';
|
||||||
|
if ((type === 'textarea' || type === 'longtext' || type === 'long text') && comp.drop_values) {
|
||||||
|
defaultValue = comp.drop_values;
|
||||||
|
}
|
||||||
|
|
||||||
|
group[controlName] = new FormControl({ value: defaultValue, disabled: isDisabled }, validators);
|
||||||
|
|
||||||
|
// Save the key so html can use it
|
||||||
|
(comp as any).generatedControlName = controlName;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add hidden ID control
|
||||||
|
group['id'] = new FormControl(null);
|
||||||
|
// Add hidden form_id control logic or just handle in submit
|
||||||
|
// group['form_id'] = new FormControl(this.formId);
|
||||||
|
|
||||||
|
this.dynamicForm = new FormGroup(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
goToAdd() {
|
||||||
|
this.dynamicForm.reset();
|
||||||
|
this.viewMode = 'add';
|
||||||
|
}
|
||||||
|
|
||||||
|
goToEdit(row: any) {
|
||||||
|
this.viewMode = 'edit';
|
||||||
|
this.dynamicForm.patchValue(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDelete(row: any) {
|
||||||
|
if (confirm('Are you sure you want to delete this record?')) {
|
||||||
|
this.dynamicFormService.deleteTransaction(row.id).subscribe(
|
||||||
|
(res) => {
|
||||||
|
this.toastr.success('Record deleted');
|
||||||
|
this.loadData(); // Refresh grid
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
console.error(err);
|
||||||
|
this.toastr.error('Failed to delete record');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
this.viewMode = 'list';
|
||||||
|
this.dynamicForm.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
if (this.dynamicForm.valid) {
|
||||||
|
const formValue = this.dynamicForm.value;
|
||||||
|
|
||||||
|
// Ensure form_id is attached
|
||||||
|
formValue.form_id = this.formId;
|
||||||
|
|
||||||
|
if (this.viewMode === 'add') {
|
||||||
|
this.dynamicFormService.createTransaction(formValue).subscribe(
|
||||||
|
(res) => {
|
||||||
|
this.toastr.success('Record Added');
|
||||||
|
this.viewMode = 'list';
|
||||||
|
this.loadData();
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
console.error(err);
|
||||||
|
this.toastr.error('Failed to add record');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Update existing
|
||||||
|
const id = formValue.id; // ID should be in the form value if we patched it
|
||||||
|
this.dynamicFormService.updateTransaction(id, this.formId, formValue).subscribe(
|
||||||
|
(res) => {
|
||||||
|
this.toastr.success('Record Updated');
|
||||||
|
this.viewMode = 'list';
|
||||||
|
this.loadData();
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
console.error(err);
|
||||||
|
this.toastr.error('Failed to update record');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Object.keys(this.dynamicForm.controls).forEach(field => {
|
||||||
|
const control = this.dynamicForm.get(field);
|
||||||
|
control.markAsTouched({ onlySelf: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type checkers
|
||||||
|
isTextField(type: string): boolean {
|
||||||
|
if (!type) return false;
|
||||||
|
const t = type.toLowerCase();
|
||||||
|
return t.includes('text') && !t.includes('area') && !t.includes('long') && !t.includes('date');
|
||||||
|
}
|
||||||
|
|
||||||
|
isTextArea(type: string): boolean {
|
||||||
|
if (!type) return false;
|
||||||
|
const t = type.toLowerCase();
|
||||||
|
return t.includes('area') || t.includes('long');
|
||||||
|
}
|
||||||
|
|
||||||
|
isCheckbox(type: string): boolean {
|
||||||
|
if (!type) return false;
|
||||||
|
return type.toLowerCase().includes('checkbox');
|
||||||
|
}
|
||||||
|
|
||||||
|
isAutocomplete(type: string): boolean {
|
||||||
|
if (!type) return false;
|
||||||
|
return type.toLowerCase().includes('auto');
|
||||||
|
}
|
||||||
|
|
||||||
|
isDropdown(type: string): boolean {
|
||||||
|
if (!type) return false;
|
||||||
|
return type.toLowerCase().includes('drop') || type.toLowerCase().includes('select');
|
||||||
|
}
|
||||||
|
|
||||||
|
isDate(type: string): boolean {
|
||||||
|
if (!type) return false;
|
||||||
|
return type.toLowerCase().includes('date');
|
||||||
|
}
|
||||||
|
|
||||||
|
isToggle(type: string): boolean {
|
||||||
|
if (!type) return false;
|
||||||
|
return type.toLowerCase().includes('toggle');
|
||||||
|
}
|
||||||
|
|
||||||
|
getDropdownOptions(values: string): string[] {
|
||||||
|
if (!values) return [];
|
||||||
|
return values.split(',').map(v => v.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/*# sourceMappingURL=dynamicform.component.css.map */
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":[],"names":[],"mappings":"","file":"dynamicform.component.css"}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<router-outlet></router-outlet>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { DynamicformComponent } from './dynamicform.component';
|
||||||
|
|
||||||
|
describe('DynamicformComponent', () => {
|
||||||
|
let component: DynamicformComponent;
|
||||||
|
let fixture: ComponentFixture<DynamicformComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ DynamicformComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(DynamicformComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-dynamicform',
|
||||||
|
templateUrl: './dynamicform.component.html',
|
||||||
|
styleUrls: ['./dynamicform.component.scss']
|
||||||
|
})
|
||||||
|
export class DynamicformComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
.button1::after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button1:hover::after {
|
||||||
|
content: "ADD ROWS";
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
background-color: #dddddd;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section p {
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clr-input {
|
||||||
|
color: #212529;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
padding: 0.75rem 0.75rem;
|
||||||
|
background-color: rgb(255, 255, 255);
|
||||||
|
margin-top: 3px;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px 5px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
}/*# sourceMappingURL=editdynamicform.component.css.map */
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["editdynamicform.component.scss","editdynamicform.component.css"],"names":[],"mappings":"AAEA;EACE,aAAA;ACDF;;ADGA;EACE,mBAAA;ACAF;;ADGA;EACE,yBAVS;EAWT,YAAA;ACAF;;ADGA;EAEE,aAAA;EACA,eAAA;ACDF;;ADIA;EACE,cAAA;EACA,yBAAA;EACA,sBAAA;EACA,wBAAA;EACA,oCAAA;EACA,eAAA;EACA,WAAA;EACA,mBAAA;ACDF;;ADGA;EACE,WAAA;EACA,gBAAA;EACA,sBAAA;EACA,kBAAA;ACAF;;ADEA;EACE,kBAAA;ACCF","file":"editdynamicform.component.css"}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
<h4 style="font-weight: 300;display: inline;"><b>DYNAMIC FORM SETUP</b></h4>
|
||||||
|
<span class="label label-light-blue" style="display: inline;margin-left: 30px;">Edit Mode</span><br>
|
||||||
|
<hr>
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<form (ngSubmit)="onSubmit()">
|
||||||
|
<div class="clr-row">
|
||||||
|
<div class="clr-col-md-4 clr-col-sm-12">
|
||||||
|
<label for="form_name">Form Name</label>
|
||||||
|
<input id="form_name" type="text" name="form_name" [(ngModel)]="rn_froms_setup.form_name"
|
||||||
|
placeholder="Enter Form Name" class="clr-input">
|
||||||
|
<div *ngFor="let error of fieldErrors">
|
||||||
|
<span *ngIf="error.field === 'form_name'" class="text text-danger">{{ error.message }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="clr-col-md-4 clr-col-sm-12">
|
||||||
|
<label for="form_desc">Form Description</label>
|
||||||
|
<input id="form_desc" type="text" name="form_desc" [(ngModel)]="rn_froms_setup.form_desc"
|
||||||
|
placeholder="Enter Form Description" class="clr-input">
|
||||||
|
<div *ngFor="let error of fieldErrors">
|
||||||
|
<span *ngIf="error.field === 'form_desc'" class="text text-danger">{{ error.message }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="clr-col-md-4 clr-col-sm-12">
|
||||||
|
<label for="related_to">Related To</label>
|
||||||
|
<select id="related_to" name="related_to" [(ngModel)]="rn_froms_setup.related_to" class="clr-dropdown">
|
||||||
|
<option value="null">Choose Type</option>
|
||||||
|
<option *ngFor="let type of related_to" [ngValue]="type">{{ type }}</option>
|
||||||
|
</select>
|
||||||
|
<div *ngFor="let error of fieldErrors">
|
||||||
|
<span *ngIf="error.field === 'related_to'" class="text text-danger">{{ error.message }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="clr-col-md-4 clr-col-sm-12">
|
||||||
|
<label for="page_event">Page Event</label>
|
||||||
|
<select id="page_event" name="page_event" [(ngModel)]="rn_froms_setup.page_event" class="clr-dropdown">
|
||||||
|
<option value="null">Choose Type</option>
|
||||||
|
<option *ngFor="let type of page_event" [ngValue]="type">{{ type }}</option>
|
||||||
|
</select>
|
||||||
|
<div *ngFor="let error of fieldErrors">
|
||||||
|
<span *ngIf="error.field === 'page_event'" class="text text-danger">{{ error.message }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="clr-col-md-4 clr-col-sm-12">
|
||||||
|
<label for="button_caption">Button Name</label>
|
||||||
|
<input id="button_caption" type="text" name="button_caption" [(ngModel)]="rn_froms_setup.button_caption"
|
||||||
|
placeholder="Enter Button Name" class="clr-input">
|
||||||
|
<div *ngFor="let error of fieldErrors">
|
||||||
|
<span *ngIf="error.field === 'button_caption'" class="text text-danger">{{ error.message }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> <!-- row end -->
|
||||||
|
<!-----H-L LINE part(FormArray For Multiple Lines)---->
|
||||||
|
|
||||||
|
<p> Component Details </p>
|
||||||
|
<hr>
|
||||||
|
<div class="clr-row">
|
||||||
|
<div class="clr-col-lg-12">
|
||||||
|
<table class="table" style="width:100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="left" style="width:200px;">Label</th>
|
||||||
|
<th class="left" style="width:200px;">Type</th>
|
||||||
|
<th class="left" style="width:200px;">Mapping</th>
|
||||||
|
<th class="left" style="width:200px;">ReadOnly</th>
|
||||||
|
<th class="left" style="width:200px;">Drop Values</th>
|
||||||
|
<!-- <th *ngIf="components" class="right" style="width:200px;">{{ components.length >= 1 ? 'Actions' : '' }}</th> -->
|
||||||
|
<th class="right" style="width:200px;">{{ components?.length >= 1 ? 'Actions' : '' }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let component of components; let i = index">
|
||||||
|
<td class="left">
|
||||||
|
<input type="text" [attr.name]="component.label" [(ngModel)]="component.label"
|
||||||
|
[ngModelOptions]="{standalone: true}" placeholder="Enter Name" class="clr-input">
|
||||||
|
</td>
|
||||||
|
<td class="left">
|
||||||
|
<select [attr.name]="component.type" [(ngModel)]="component.type"
|
||||||
|
[ngModelOptions]="{standalone: true}" class="clr-dropdown">
|
||||||
|
<option value="null">Choose Field Type</option>
|
||||||
|
<option *ngFor="let type of field_type" [ngValue]="type">{{ type }}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="left">
|
||||||
|
<select [attr.name]="component.mapping" [(ngModel)]="component.mapping"
|
||||||
|
[ngModelOptions]="{standalone: true}" class="clr-dropdown">
|
||||||
|
<option value="null">Choose Mapping</option>
|
||||||
|
<option *ngFor="let mapping of getFilteredMappings(i)" [ngValue]="mapping.value">{{
|
||||||
|
mapping.label
|
||||||
|
}}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="left">
|
||||||
|
<div class="toggle-switch">
|
||||||
|
<input type="checkbox" [attr.id]="'readonly' + i" [attr.name]="component.readonly"
|
||||||
|
[(ngModel)]="component.readonly" [ngModelOptions]="{standalone: true}" />
|
||||||
|
<label [for]="'readonly' + i"></label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="left">
|
||||||
|
<ng-container *ngIf="component.type === 'dropdown'">
|
||||||
|
<div style="display:flex; flex-wrap:wrap; gap:5px; margin-bottom:5px;">
|
||||||
|
<span *ngFor="let val of getDropValuesArray(i)" class="label label-blue">
|
||||||
|
{{ val }}
|
||||||
|
<clr-icon shape="times" (click)="removeDropValue(i, val)"
|
||||||
|
style="cursor:pointer; margin-left:5px;"></clr-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input type="text" placeholder="Type & Enter"
|
||||||
|
(keydown.enter)="$event.preventDefault(); addDropValue(i, $event)"
|
||||||
|
style="width:180px" class="clr-input">
|
||||||
|
<input type="text" [attr.name]="component.drop_values"
|
||||||
|
[(ngModel)]="component.drop_values" [ngModelOptions]="{standalone: true}"
|
||||||
|
hidden>
|
||||||
|
</ng-container>
|
||||||
|
</td>
|
||||||
|
<td class="right">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" form-control class="btn btn-primary">UPDATE</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
$bg-color: #dddddd;
|
||||||
|
|
||||||
|
.button1::after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
.button1:hover::after {
|
||||||
|
content: "ADD ROWS";
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
background-color: $bg-color;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section p {
|
||||||
|
//color: white;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clr-input {
|
||||||
|
color: #212529;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
padding: 0.75rem 0.75rem;
|
||||||
|
background-color:rgb(255, 255, 255);
|
||||||
|
margin-top: 3px;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
select{
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px 5px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { EditdynamicformComponent } from './editdynamicform.component';
|
||||||
|
|
||||||
|
describe('EditdynamicformComponent', () => {
|
||||||
|
let component: EditdynamicformComponent;
|
||||||
|
let fixture: ComponentFixture<EditdynamicformComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ EditdynamicformComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(EditdynamicformComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { Rn_Forms_Component_Setup } from 'src/app/models/fnd/Rn_Forms_Component_Setup';
|
||||||
|
import { Rn_Forms_Setup } from 'src/app/models/fnd/Rn_Forms_Setup';
|
||||||
|
import { ValidationError } from 'src/app/models/fnd/ValidationError';
|
||||||
|
import { DynamicformService } from 'src/app/services/fnd/dynamicform.service';
|
||||||
|
import { Mapping } from "src/app/models/fnd/Mapping";
|
||||||
|
@Component({
|
||||||
|
selector: 'app-editdynamicform',
|
||||||
|
templateUrl: './editdynamicform.component.html',
|
||||||
|
styleUrls: ['./editdynamicform.component.scss']
|
||||||
|
})
|
||||||
|
export class EditdynamicformComponent implements OnInit {
|
||||||
|
updated = false;
|
||||||
|
fieldErrors: ValidationError[] = [];
|
||||||
|
rn_froms_setup: Rn_Forms_Setup;
|
||||||
|
components: Rn_Forms_Component_Setup[];
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
related_to = ["Menu", "Related To"];
|
||||||
|
page_event = ["OnClick", "OnBlur"];
|
||||||
|
field_type = [
|
||||||
|
"textfield",
|
||||||
|
"dropdown",
|
||||||
|
"date",
|
||||||
|
"checkbox",
|
||||||
|
"textarea",
|
||||||
|
"togglebutton",
|
||||||
|
];
|
||||||
|
// mappings: Mapping[];
|
||||||
|
|
||||||
|
mappings = [
|
||||||
|
{ label: 'textfield1', value: 'comp1' },
|
||||||
|
{ label: 'textfield2', value: 'comp2' },
|
||||||
|
{ label: 'textfield3', value: 'comp3' },
|
||||||
|
{ label: 'textfield4', value: 'comp4' },
|
||||||
|
{ label: 'textfield5', value: 'comp5' },
|
||||||
|
{ label: 'textfield6', value: 'comp6' },
|
||||||
|
{ label: 'textfield7', value: 'comp7' },
|
||||||
|
{ label: 'textfield8', value: 'comp8' },
|
||||||
|
{ label: 'textfield9', value: 'comp9' },
|
||||||
|
{ label: 'textfield10', value: 'comp10' },
|
||||||
|
{ label: 'textfield11', value: 'comp11' },
|
||||||
|
{ label: 'textfield12', value: 'comp12' },
|
||||||
|
{ label: 'textfield13', value: 'comp13' },
|
||||||
|
{ label: 'textfield14', value: 'comp14' },
|
||||||
|
{ label: 'textfield15', value: 'comp15' },
|
||||||
|
{ label: 'textfield16', value: 'comp16' },
|
||||||
|
{ label: 'textfield17', value: 'comp17' },
|
||||||
|
{ label: 'textfield18', value: 'comp18' },
|
||||||
|
{ label: 'textfield19', value: 'comp19' },
|
||||||
|
{ label: 'textfield20', value: 'comp20' },
|
||||||
|
{ label: 'textfield21', value: 'comp21' },
|
||||||
|
{ label: 'textfield22', value: 'comp22' },
|
||||||
|
{ label: 'textfield23', value: 'comp23' },
|
||||||
|
{ label: 'textfield24', value: 'comp24' },
|
||||||
|
{ label: 'textfield25', value: 'comp25' },
|
||||||
|
{ label: 'textfield26', value: 'comp26' },
|
||||||
|
{ label: 'longtext1', value: 'comp_l27' },
|
||||||
|
{ label: 'longtext2', value: 'comp_l28' },
|
||||||
|
{ label: 'longtext3', value: 'comp_l29' },
|
||||||
|
{ label: 'longtext4', value: 'comp_l30' }
|
||||||
|
];
|
||||||
|
constructor(private router: Router,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private dynamicservice: DynamicformService,
|
||||||
|
private httpService: HttpClient) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
// this.getMapings();
|
||||||
|
this.rn_froms_setup = new Rn_Forms_Setup();
|
||||||
|
this.id = this.route.snapshot.params["id"];
|
||||||
|
console.log("update with id = ", this.id);
|
||||||
|
this.getById(this.id);
|
||||||
|
}
|
||||||
|
getById(id: number) {
|
||||||
|
this.dynamicservice.getById(id).subscribe((data) => {
|
||||||
|
this.rn_froms_setup = data;
|
||||||
|
this.components = data.components;
|
||||||
|
console.log('component length = ', this.components.length.toString());
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
update() {
|
||||||
|
this.fieldErrors = [];
|
||||||
|
this.dynamicservice.update(this.id, this.rn_froms_setup).subscribe(
|
||||||
|
(data) => {
|
||||||
|
console.log(data);
|
||||||
|
this.router.navigate(["../../all"], { relativeTo: this.route });
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
/* (error: HttpErrorResponse) => { */
|
||||||
|
console.log(error);
|
||||||
|
const objectArray = Object.entries(error.error.fieldErrors);
|
||||||
|
objectArray.forEach(([k, v]) => {
|
||||||
|
console.log(k);
|
||||||
|
console.log(v);
|
||||||
|
this.fieldErrors.push({ field: k, message: v });
|
||||||
|
});
|
||||||
|
console.log(this.fieldErrors); // this will come from backend
|
||||||
|
this.rn_froms_setup = new Rn_Forms_Setup();
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
onSubmit() {
|
||||||
|
this.updated = true;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilteredMappings(index: number) {
|
||||||
|
if (!this.components) return this.mappings;
|
||||||
|
|
||||||
|
const selectedValues = this.components
|
||||||
|
.map((comp, i) => {
|
||||||
|
if (i === index) return null;
|
||||||
|
return comp.mapping;
|
||||||
|
})
|
||||||
|
.filter(val => val !== null && val !== 'null' && val !== '');
|
||||||
|
|
||||||
|
return this.mappings.filter(m => !selectedValues.includes(m.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag Input Helpers for Edit Mode
|
||||||
|
getDropValuesArray(index: number): string[] {
|
||||||
|
const component = this.components[index];
|
||||||
|
const val = component.drop_values;
|
||||||
|
if (!val) return [];
|
||||||
|
return val.split(',').map(s => s.trim()).filter(s => s.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
addDropValue(index: number, event: any) {
|
||||||
|
const value = event.target.value;
|
||||||
|
if (!value) return;
|
||||||
|
|
||||||
|
const component = this.components[index];
|
||||||
|
const currentArr = this.getDropValuesArray(index);
|
||||||
|
|
||||||
|
if (!currentArr.includes(value.trim())) {
|
||||||
|
currentArr.push(value.trim());
|
||||||
|
component.drop_values = currentArr.join(',');
|
||||||
|
}
|
||||||
|
event.target.value = ''; // Clear input
|
||||||
|
}
|
||||||
|
|
||||||
|
removeDropValue(index: number, valueToRemove: string) {
|
||||||
|
const component = this.components[index];
|
||||||
|
let currentArr = this.getDropValuesArray(index);
|
||||||
|
currentArr = currentArr.filter(v => v !== valueToRemove);
|
||||||
|
component.drop_values = currentArr.join(',');
|
||||||
|
}
|
||||||
|
// this.httpService
|
||||||
|
// .get<Mapping[]>("./assets/json/form-setup-mapping.json")
|
||||||
|
// .subscribe(
|
||||||
|
// (data) => {
|
||||||
|
// console.log(data);
|
||||||
|
// this.mappings = data;
|
||||||
|
// },
|
||||||
|
// (err) => console.log(err)
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
back() {
|
||||||
|
this.router.navigate(["../../all"], { relativeTo: this.route });
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user