Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface IFieldControl<T extends UntypedFormControl | UntypedFormGroup |
model: any;
subject: Subject<void>;
visibility: boolean;
csvEnabled: boolean;
}

export interface IFieldIndexControl<T extends UntypedFormControl | UntypedFormGroup> {
Expand Down Expand Up @@ -643,7 +644,8 @@ export class FieldForm {
open: this.lvl === 0,
subject: new Subject(),
visibility: true,
model: null
model: null,
csvEnabled: false
};
item.visibility = !item.hide && !item.hidden && !item.autocalculate;
item.preset = field.default;
Expand Down Expand Up @@ -686,6 +688,7 @@ export class FieldForm {
if (field.isArray && !field.isRef) {
item.control = this.createArrayControl();
item.list = [];
item.csvEnabled = true;
if (field.remoteLink) {
item.fileUploading = true;
}
Expand Down Expand Up @@ -715,6 +718,15 @@ export class FieldForm {
item.control = this.createArrayControl();
item.list = [];
item.fields = field.fields;
item.csvEnabled = true;
if(item.fields) {
for (const fld of item.fields) {
if(fld.isRef) {
item.csvEnabled = false;
}
}
}

if (item.preset && item.preset.length) {
for (let index = 0; index < item.preset.length; index++) {
const preset = item.preset[index];
Expand Down Expand Up @@ -887,6 +899,43 @@ export class FieldForm {
}
}

public setFromCsv(control: IFieldControl<any>, rows: Record<string, string>[]) {
const cFieldName = control.field.name;

const values = rows.reduce<string[]>((acc, row) => {
const raw = row[cFieldName];
const split = raw?.includes(';')
? raw.split(';').map(v => v.trim().replace(/^\(/, '').replace(/\)$/, ''))
: [raw?.trim()];
return acc.concat(split);
}, []);

if(control.isArray) {
//remove items
control.list?.forEach(controlListItem => this.removeItem(control, controlListItem));
//add items
values.forEach(() => this.addItem(control));
}

//fill
if(!control.isRef && control.isArray) {
for(let i = 0; i< values.length; i++) {
control.list && control.list[i].control.setValue(values[i]);
}
} else if(control.isRef && control.isArray) {
if(control.list) {
for(let i = 0; i < control.list.length; i++) {
const listItem = control.list[i];
listItem.model.controls.forEach((ctr: any) => {
listItem.model.setFromCsv(ctr, [rows[i]]);
})
}
}
} else if(!control.isArray && !control.isRef) {
control.control.setValue(values[0]);
}
}

public removeGroup(item: IFieldControl<any>) {
item.control = null;
item.model?.destroy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -499,45 +499,59 @@

<!-- array of simple fields -->
<div *ngIf="ifSimpleArray(item)" class="form-field-array" [attr.array-invalid]="item.control?.invalid">
<div class="label-field" id="detail-section">
<div
*ngIf="!isChildSchema && formModel.controls && !isEditMode"
class="page-number"
>
{{ i }}/{{ formModel.controls.length - 1 }}
</div>
<div
[attr.default]="
item.default &&
!item.isPreset &&
!item.control.dirty
"
[attr.field-path]="item?.path"
>
<span
*ngIf="ifRequiredField(item)"
class="required-field"
>*</span
<div class="label-field csv-button" id="detail-section">
<div>
<div
*ngIf="!isChildSchema && formModel.controls && !isEditMode"
class="page-number"
>
{{ item.description }}
{{ i }}/{{ formModel.controls.length - 1 }}
</div>
<div
[attr.default]="
item.default &&
!item.isPreset &&
!item.control.dirty
"
[attr.field-path]="item?.path"
>
<span
*ngIf="ifRequiredField(item)"
class="required-field"
>*</span
>
{{ item.description }}
</div>
<div
*ngIf="
!isChildSchema &&
formModel.controls &&
i !== formModel.controls.length - 1
"
class="delimiter-to-next-page"
></div>
<div
*ngIf="
!isChildSchema &&
formModel.controls &&
i !== formModel.controls.length - 1
"
class="next-page-number"
>
{{ i + 1 }}
</div>
</div>
<div
*ngIf="
!isChildSchema &&
formModel.controls &&
i !== formModel.controls.length - 1
"
class="delimiter-to-next-page"
></div>
<div
*ngIf="
!isChildSchema &&
formModel.controls &&
i !== formModel.controls.length - 1
"
class="next-page-number"
>
{{ i + 1 }}
<div *ngIf="item.csvEnabled" class="csv-button-area">
<span class="invalid-field-label">
Quickly prefill the data using a CSV file
</span>
<button
class="guardian-button guardian-button-secondary"
type="button"
(mousedown)="$event.stopPropagation()"
(click)="stopAndTrigger($event, item)">
Upload CSV
</button>
</div>
</div>

Expand Down Expand Up @@ -866,7 +880,7 @@
>
<p-accordionTab [(selected)]="item.open" (selectedChange)="onAccordionSelectEvent($event, item)">
<ng-template pTemplate="header">
<div class="group-label-field" id="detail-section" [attr.accordiontabid]="item.id">
<div class="group-label-field csv-button csv-button-padding" id="detail-section" [attr.accordiontabid]="item.id">
<div
*ngIf="
!isChildSchema &&
Expand Down Expand Up @@ -1089,9 +1103,19 @@
>
{{ i + 1 }}
</div>
<div *ngIf="item.csvEnabled" class="csv-button-area" >
<span class="invalid-field-label">
Quickly prefill the data using a CSV file
</span>
<button
type="button"
(click)="stopAndTrigger($event, item)"
class="guardian-button guardian-button-secondary">Upload CSV
</button>
</div>
</div>
</ng-template>

</ng-template>
<ng-container *ngIf="item.open">
<div
*ngIf="
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ form {
}
}

.csv-button {
display: flex;
justify-content: space-between;
}

.csv-button-padding {
padding-right: 40px !important;
}

.csv-button-area {
display: flex;
gap: 8px;

button {
padding: 6px 16px;
}
}

.group-label-field {
font-family: Poppins;
font-size: 16px;
Expand Down Expand Up @@ -492,4 +510,4 @@ form::ng-deep {

.form-field-input {
height: auto !important;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -986,5 +986,57 @@ export class SchemaFormComponent implements OnInit {
}
return item.model.controls?.filter((f: any) => f.type !== 'null' && !f.hidden);
}

public async onFileSelected(file: File, item: IFieldControl<any>) {
const rows = await this.parseCsvFile(file);
this.formModel.setFromCsv(item, rows);
}

public stopAndTrigger(event: MouseEvent, item: IFieldControl<any>): void {
event.stopPropagation();
event.preventDefault();

const input = document.createElement('input');
input.type = 'file';
input.accept = '.csv';

input.addEventListener('change', (e: Event) => {
const file = (e.target as HTMLInputElement).files?.[0];
if (file) {
this.onFileSelected(file, item);
}
});

input.click();
}

public async parseCsvFile(file: File): Promise<Record<string, string>[]> {
return new Promise((resolve, reject) => {
if (!file) return reject(new Error('No file selected'));

const reader = new FileReader();

reader.onload = () => {
const text = reader.result as string;
const lines = text.trim().split('\n');
const headers = lines[0].split(',').map(h => h.trim());

const rows = lines.slice(1).map(line => {
const values = line.split(',').map(v => v.trim());
const row: Record<string, string> = {};
headers.forEach((header, i) => {
row[header] = values[i] ?? '';
});
return row;
});

resolve(rows);
};

reader.onerror = () => reject(reader.error);

reader.readAsText(file);
});
}
}

Loading