This commit is contained in:
Gaurav Kumar
2025-12-27 08:45:54 +05:30
parent 86612c78c9
commit b81b67e8ab
27 changed files with 1732 additions and 0 deletions

View File

@@ -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 */

View File

@@ -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"}

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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();
});
});

View File

@@ -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');
}
}
);
}
}

View File

@@ -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 */

View File

@@ -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"}

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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();
});
});

View File

@@ -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');
}
});
}
}

View File

@@ -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>

View File

@@ -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; }

View File

@@ -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());
}
}

View File

@@ -0,0 +1 @@
/*# sourceMappingURL=dynamicform.component.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":[],"names":[],"mappings":"","file":"dynamicform.component.css"}

View File

@@ -0,0 +1 @@
<router-outlet></router-outlet>

View File

@@ -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();
});
});

View File

@@ -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 {
}
}

View File

@@ -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 */

View File

@@ -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"}

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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();
});
});

View File

@@ -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 });
}
}