mirror of
https://github.com/frappe/books.git
synced 2024-12-23 11:29:03 +00:00
incr: add pick columns modal
This commit is contained in:
parent
b24577f9fc
commit
6e64f4f93d
@ -12,7 +12,7 @@ import {
|
|||||||
} from 'schemas/types';
|
} from 'schemas/types';
|
||||||
import { generateCSV, parseCSV } from 'utils/csvParser';
|
import { generateCSV, parseCSV } from 'utils/csvParser';
|
||||||
|
|
||||||
type TemplateField = Field & {
|
export type TemplateField = Field & {
|
||||||
schemaName: string;
|
schemaName: string;
|
||||||
schemaLabel: string;
|
schemaLabel: string;
|
||||||
fieldKey: string;
|
fieldKey: string;
|
||||||
@ -32,7 +32,7 @@ const skippedFieldsTypes: FieldType[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tool that
|
* Tool that
|
||||||
* - Can make bulk entries for any kind of Doc
|
* - Can make bulk entries for any kind of Doc
|
||||||
* - Takes in unstructured CSV data, converts it into Docs
|
* - Takes in unstructured CSV data, converts it into Docs
|
||||||
* - Saves and or Submits the converted Docs
|
* - Saves and or Submits the converted Docs
|
||||||
@ -45,7 +45,7 @@ export class Importer {
|
|||||||
* List of template fields that have been assigned a column, in
|
* List of template fields that have been assigned a column, in
|
||||||
* the order they have been assigned.
|
* the order they have been assigned.
|
||||||
*/
|
*/
|
||||||
assignedTemplateFields: string[];
|
assignedTemplateFields: (string | null)[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map of all the template fields that can be imported.
|
* Map of all the template fields that can be imported.
|
||||||
@ -59,11 +59,6 @@ export class Importer {
|
|||||||
*/
|
*/
|
||||||
templateFieldsPicked: Map<string, boolean>;
|
templateFieldsPicked: Map<string, boolean>;
|
||||||
|
|
||||||
/**
|
|
||||||
* Map of Fields that have been assigned columns
|
|
||||||
*/
|
|
||||||
templateFieldsAssigned: Map<string, number>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the schema type being imported has table fields
|
* Whether the schema type being imported has table fields
|
||||||
*/
|
*/
|
||||||
@ -91,12 +86,10 @@ export class Importer {
|
|||||||
this.assignedTemplateFields = templateFields.map((f) => f.fieldKey);
|
this.assignedTemplateFields = templateFields.map((f) => f.fieldKey);
|
||||||
this.templateFieldsMap = new Map();
|
this.templateFieldsMap = new Map();
|
||||||
this.templateFieldsPicked = new Map();
|
this.templateFieldsPicked = new Map();
|
||||||
this.templateFieldsAssigned = new Map();
|
|
||||||
|
|
||||||
templateFields.forEach((f, i) => {
|
templateFields.forEach((f, i) => {
|
||||||
this.templateFieldsMap.set(f.fieldKey, f);
|
this.templateFieldsMap.set(f.fieldKey, f);
|
||||||
this.templateFieldsPicked.set(f.fieldKey, true);
|
this.templateFieldsPicked.set(f.fieldKey, true);
|
||||||
this.templateFieldsAssigned.set(f.fieldKey, i);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,6 +108,7 @@ export class Importer {
|
|||||||
addRow() {
|
addRow() {
|
||||||
const valueRow: ValueMatrix[number] = this.assignedTemplateFields.map(
|
const valueRow: ValueMatrix[number] = this.assignedTemplateFields.map(
|
||||||
(key) => {
|
(key) => {
|
||||||
|
key ??= '';
|
||||||
const { fieldtype } = this.templateFieldsMap.get(key) ?? {};
|
const { fieldtype } = this.templateFieldsMap.get(key) ?? {};
|
||||||
let value = null;
|
let value = null;
|
||||||
if (fieldtype) {
|
if (fieldtype) {
|
||||||
|
@ -32,10 +32,19 @@
|
|||||||
p-4
|
p-4
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<FormControl
|
<AutoComplete
|
||||||
:df="importableDf"
|
:df="{
|
||||||
|
fieldname: 'importType',
|
||||||
|
label: t`Import Type`,
|
||||||
|
fieldtype: 'AutoComplete',
|
||||||
|
options: importableSchemaNames.map((value) => ({
|
||||||
|
value,
|
||||||
|
label: fyo.schemaMap[value]?.label ?? value,
|
||||||
|
})),
|
||||||
|
}"
|
||||||
input-class="bg-transparent text-gray-900 text-base"
|
input-class="bg-transparent text-gray-900 text-base"
|
||||||
class="w-40 bg-gray-100 rounded"
|
class="w-40"
|
||||||
|
:border="true"
|
||||||
:value="importType"
|
:value="importType"
|
||||||
size="small"
|
size="small"
|
||||||
@change="setImportType"
|
@change="setImportType"
|
||||||
@ -95,18 +104,19 @@
|
|||||||
<!-- Grid -->
|
<!-- Grid -->
|
||||||
<div
|
<div
|
||||||
class="overflow-x-scroll gap-4 p-4 grid"
|
class="overflow-x-scroll gap-4 p-4 grid"
|
||||||
:style="`grid-template-columns: repeat(${pickedArray.length}, 10rem)`"
|
:style="`grid-template-columns: repeat(${columnCount}, 10rem)`"
|
||||||
>
|
>
|
||||||
<!-- Grid Title Row Cells, Allow Column Selection -->
|
<!-- Grid Title Row Cells, Allow Column Selection -->
|
||||||
<AutoComplete
|
<AutoComplete
|
||||||
class="flex-shrink-0"
|
class="flex-shrink-0"
|
||||||
v-for="(_, index) in pickedArray"
|
:class="importer.assignedTemplateFields[index] ? '' : ''"
|
||||||
|
v-for="index in columnIterator"
|
||||||
size="small"
|
size="small"
|
||||||
:border="true"
|
:border="true"
|
||||||
:key="index"
|
:key="index"
|
||||||
:df="gridColumnTitleDf"
|
:df="gridColumnTitleDf"
|
||||||
:value="importer.assignedTemplateFields[index]"
|
:value="importer.assignedTemplateFields[index]"
|
||||||
@change="(v:string) => importer.assignedTemplateFields[index] = v"
|
@change="(v: string | null) => importer.assignedTemplateFields[index] = v"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Grid Value Row Cells, Allow Editing Values -->
|
<!-- Grid Value Row Cells, Allow Editing Values -->
|
||||||
@ -115,21 +125,36 @@
|
|||||||
v-for="(val, cidx) of row"
|
v-for="(val, cidx) of row"
|
||||||
:key="`cell-${ridx}-${cidx}`"
|
:key="`cell-${ridx}-${cidx}`"
|
||||||
>
|
>
|
||||||
<div v-if="!importer.assignedTemplateFields[cidx]">
|
<!-- Raw Data Field if Column is Not Assigned -->
|
||||||
{{ val.value }}
|
<Data
|
||||||
</div>
|
v-if="!importer.assignedTemplateFields[cidx]"
|
||||||
|
:title="val.rawValue ?? val.value"
|
||||||
|
:df="{
|
||||||
|
fieldname: 'tempField',
|
||||||
|
label: t`Temporary`,
|
||||||
|
placeholder: t`Select column`,
|
||||||
|
}"
|
||||||
|
size="small"
|
||||||
|
:border="true"
|
||||||
|
:value="val.value == null ? '' : String(val.value)"
|
||||||
|
:read-only="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- FormControl Field if Column is Assigned -->
|
||||||
<FormControl
|
<FormControl
|
||||||
v-else
|
v-else
|
||||||
|
:title="val.error ? val.rawValue ?? val.value : val.value"
|
||||||
:df="
|
:df="
|
||||||
importer.templateFieldsMap.get(
|
importer.templateFieldsMap.get(
|
||||||
importer.assignedTemplateFields[cidx]
|
importer.assignedTemplateFields[cidx]!
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
size="small"
|
size="small"
|
||||||
:rows="1"
|
:rows="1"
|
||||||
:border="true"
|
:border="true"
|
||||||
:value="val.value"
|
:value="val.error ? null : val.value"
|
||||||
@change="(value: DocValue)=> {
|
@change="(value: DocValue)=> {
|
||||||
|
importer.valueMatrix[ridx][cidx]!.error = false
|
||||||
importer.valueMatrix[ridx][cidx]!.value = value
|
importer.valueMatrix[ridx][cidx]!.value = value
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
@ -208,6 +233,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- How to Use Link -->
|
||||||
<div
|
<div
|
||||||
v-if="!importType"
|
v-if="!importType"
|
||||||
class="flex justify-center h-full w-full items-center mb-16"
|
class="flex justify-center h-full w-full items-center mb-16"
|
||||||
@ -220,28 +247,85 @@
|
|||||||
</HowTo>
|
</HowTo>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Loading Bar when Saving Docs -->
|
||||||
<Loading
|
<Loading
|
||||||
v-if="isMakingEntries"
|
v-if="isMakingEntries"
|
||||||
:open="isMakingEntries"
|
:open="isMakingEntries"
|
||||||
:percent="percentLoading"
|
:percent="percentLoading"
|
||||||
:message="messageLoading"
|
:message="messageLoading"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- Pick Column Modal -->
|
||||||
|
<Modal
|
||||||
|
:open-modal="showColumnPicker"
|
||||||
|
@closemodal="showColumnPicker = false"
|
||||||
|
>
|
||||||
|
<div class="w-form">
|
||||||
|
<!-- Pick Column Header -->
|
||||||
|
<FormHeader :form-title="t`Pick Import Columns`" />
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<!-- Pick Column Checkboxes -->
|
||||||
|
<div
|
||||||
|
class="p-4 max-h-80 overflow-auto custom-scroll"
|
||||||
|
v-for="[key, value] of columnPickerFieldsMap.entries()"
|
||||||
|
:key="key"
|
||||||
|
>
|
||||||
|
<h2 class="text-sm font-semibold text-gray-800">
|
||||||
|
{{ key }}
|
||||||
|
</h2>
|
||||||
|
<div class="grid grid-cols-3 border rounded mt-1">
|
||||||
|
<div
|
||||||
|
v-for="tf of value"
|
||||||
|
:key="tf.fieldKey"
|
||||||
|
class="flex items-center"
|
||||||
|
>
|
||||||
|
<Check
|
||||||
|
:df="{
|
||||||
|
fieldname: tf.fieldname,
|
||||||
|
label: tf.label,
|
||||||
|
}"
|
||||||
|
:show-label="true"
|
||||||
|
:read-only="tf.required"
|
||||||
|
:value="importer.templateFieldsPicked.get(tf.fieldKey)"
|
||||||
|
@change="(value:boolean) => pickColumn(tf.fieldKey, value)"
|
||||||
|
/>
|
||||||
|
<p v-if="tf.required" class="w-0 text-red-600 -ml-4">*</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pick Column Footer -->
|
||||||
|
<hr />
|
||||||
|
<div class="p-4 flex justify-between items-center">
|
||||||
|
<p class="text-sm text-gray-600">
|
||||||
|
{{ t`${numColumnsPicked} fields selected` }}
|
||||||
|
</p>
|
||||||
|
<Button type="primary" @click="showColumnPicker = false">{{
|
||||||
|
t`Done`
|
||||||
|
}}</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { log } from 'console';
|
|
||||||
import { DocValue } from 'fyo/core/types';
|
import { DocValue } from 'fyo/core/types';
|
||||||
import { Action as BaseAction } from 'fyo/model/types';
|
import { Action as BaseAction } from 'fyo/model/types';
|
||||||
import { ValidationError } from 'fyo/utils/errors';
|
import { ValidationError } from 'fyo/utils/errors';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { FieldTypeEnum, OptionField, SelectOption } from 'schemas/types';
|
import { OptionField, SelectOption } from 'schemas/types';
|
||||||
import Button from 'src/components/Button.vue';
|
import Button from 'src/components/Button.vue';
|
||||||
import AutoComplete from 'src/components/Controls/AutoComplete.vue';
|
import AutoComplete from 'src/components/Controls/AutoComplete.vue';
|
||||||
|
import Check from 'src/components/Controls/Check.vue';
|
||||||
|
import Data from 'src/components/Controls/Data.vue';
|
||||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||||
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
||||||
|
import FormHeader from 'src/components/FormHeader.vue';
|
||||||
import HowTo from 'src/components/HowTo.vue';
|
import HowTo from 'src/components/HowTo.vue';
|
||||||
|
import Modal from 'src/components/Modal.vue';
|
||||||
import PageHeader from 'src/components/PageHeader.vue';
|
import PageHeader from 'src/components/PageHeader.vue';
|
||||||
import { Importer } from 'src/importer';
|
import { Importer, TemplateField } from 'src/importer';
|
||||||
import { fyo } from 'src/initFyo';
|
import { fyo } from 'src/initFyo';
|
||||||
import { getSavePath, saveData, selectFile } from 'src/utils/ipcCalls';
|
import { getSavePath, saveData, selectFile } from 'src/utils/ipcCalls';
|
||||||
import { docsPathMap } from 'src/utils/misc';
|
import { docsPathMap } from 'src/utils/misc';
|
||||||
@ -255,6 +339,7 @@ type Action = Pick<BaseAction, 'condition' | 'component'> & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type DataImportData = {
|
type DataImportData = {
|
||||||
|
showColumnPicker: boolean;
|
||||||
canReset: boolean;
|
canReset: boolean;
|
||||||
complete: boolean;
|
complete: boolean;
|
||||||
names: string[];
|
names: string[];
|
||||||
@ -275,9 +360,14 @@ export default defineComponent({
|
|||||||
HowTo,
|
HowTo,
|
||||||
Loading,
|
Loading,
|
||||||
AutoComplete,
|
AutoComplete,
|
||||||
|
Data,
|
||||||
|
Modal,
|
||||||
|
FormHeader,
|
||||||
|
Check,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
showColumnPicker: false,
|
||||||
canReset: false,
|
canReset: false,
|
||||||
complete: false,
|
complete: false,
|
||||||
names: ['Bat', 'Baseball', 'Other Shit'],
|
names: ['Bat', 'Baseball', 'Other Shit'],
|
||||||
@ -292,13 +382,53 @@ export default defineComponent({
|
|||||||
mounted() {
|
mounted() {
|
||||||
if (fyo.store.isDevelopment) {
|
if (fyo.store.isDevelopment) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.bew = this;
|
window.iw = this;
|
||||||
|
this.setImportType('Item');
|
||||||
|
this.showColumnPicker = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
columnCount(): number {
|
||||||
|
let vmColumnCount = 0;
|
||||||
|
if (this.importer.valueMatrix.length) {
|
||||||
|
vmColumnCount = this.importer.valueMatrix[0].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.max(this.numColumnsPicked, vmColumnCount);
|
||||||
|
},
|
||||||
|
columnIterator(): number[] {
|
||||||
|
return Array(this.columnCount)
|
||||||
|
.fill(null)
|
||||||
|
.map((_, i) => i);
|
||||||
|
},
|
||||||
hasImporter(): boolean {
|
hasImporter(): boolean {
|
||||||
return !!this.nullOrImporter;
|
return !!this.nullOrImporter;
|
||||||
},
|
},
|
||||||
|
numColumnsPicked(): number {
|
||||||
|
return [...this.importer.templateFieldsPicked.values()].filter(Boolean)
|
||||||
|
.length;
|
||||||
|
},
|
||||||
|
columnPickerFieldsMap(): Map<string, TemplateField[]> {
|
||||||
|
const map: Map<string, TemplateField[]> = new Map();
|
||||||
|
|
||||||
|
for (const value of this.importer.templateFieldsMap.values()) {
|
||||||
|
let label = value.schemaLabel;
|
||||||
|
if (value.parentSchemaChildField) {
|
||||||
|
label = `${value.parentSchemaChildField.label} (${value.schemaLabel})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!map.has(label)) {
|
||||||
|
map.set(label, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
map.get(label)!.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
},
|
||||||
|
requiredColumnsNotPicked(): string[] {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
importer(): Importer {
|
importer(): Importer {
|
||||||
if (!this.nullOrImporter) {
|
if (!this.nullOrImporter) {
|
||||||
throw new ValidationError(this.t`Importer not set, reload tool`, false);
|
throw new ValidationError(this.t`Importer not set, reload tool`, false);
|
||||||
@ -358,6 +488,14 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pickColumnsAction = {
|
||||||
|
component: {
|
||||||
|
template: '<span>{{ t`Pick Import Columns` }}</span>',
|
||||||
|
},
|
||||||
|
condition: () => true,
|
||||||
|
action: () => (this.showColumnPicker = true),
|
||||||
|
};
|
||||||
|
|
||||||
const cancelAction = {
|
const cancelAction = {
|
||||||
component: {
|
component: {
|
||||||
template: '<span class="text-red-700" >{{ t`Cancel` }}</span>',
|
template: '<span class="text-red-700" >{{ t`Cancel` }}</span>',
|
||||||
@ -365,7 +503,7 @@ export default defineComponent({
|
|||||||
condition: () => true,
|
condition: () => true,
|
||||||
action: this.clear,
|
action: this.clear,
|
||||||
};
|
};
|
||||||
actions.push(cancelAction);
|
actions.push(pickColumnsAction, cancelAction);
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
},
|
},
|
||||||
@ -392,32 +530,13 @@ export default defineComponent({
|
|||||||
const schemaName = this.importer.schemaName;
|
const schemaName = this.importer.schemaName;
|
||||||
return fyo.schemaMap[schemaName]?.isSubmittable ?? false;
|
return fyo.schemaMap[schemaName]?.isSubmittable ?? false;
|
||||||
},
|
},
|
||||||
importableDf(): OptionField {
|
|
||||||
return {
|
|
||||||
fieldname: 'importType',
|
|
||||||
label: this.t`Import Type`,
|
|
||||||
fieldtype: FieldTypeEnum.AutoComplete,
|
|
||||||
options: Object.keys(this.labelSchemaNameMap).map((k) => ({
|
|
||||||
value: k,
|
|
||||||
label: k,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
labelSchemaNameMap(): Record<string, string> {
|
|
||||||
return this.importableSchemaNames
|
|
||||||
.map((i) => ({
|
|
||||||
name: i,
|
|
||||||
label: fyo.schemaMap[i]?.label ?? i,
|
|
||||||
}))
|
|
||||||
.reduce((acc, { name, label }) => {
|
|
||||||
acc[label] = name;
|
|
||||||
return acc;
|
|
||||||
}, {} as Record<string, string>);
|
|
||||||
},
|
|
||||||
gridColumnTitleDf(): OptionField {
|
gridColumnTitleDf(): OptionField {
|
||||||
const options: SelectOption[] = [];
|
const options: SelectOption[] = [];
|
||||||
for (const field of this.importer.templateFieldsMap.values()) {
|
for (const field of this.importer.templateFieldsMap.values()) {
|
||||||
const value = field.fieldKey;
|
const value = field.fieldKey;
|
||||||
|
if (!this.importer.templateFieldsPicked.get(value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let label = field.label;
|
let label = field.label;
|
||||||
if (field.parentSchemaChildField) {
|
if (field.parentSchemaChildField) {
|
||||||
@ -435,7 +554,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
pickedArray(): string[] {
|
pickedArray(): string[] {
|
||||||
return [...this.importer.templateFieldsPicked.entries()]
|
return [...this.importer.templateFieldsPicked.entries()]
|
||||||
.filter(([key, picked]) => picked)
|
.filter(([_, picked]) => picked)
|
||||||
.map(([key, _]) => key);
|
.map(([key, _]) => key);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -452,6 +571,20 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
log: console.log,
|
log: console.log,
|
||||||
|
pickColumn(fieldKey: string, value: boolean): void {
|
||||||
|
this.importer.templateFieldsPicked.set(fieldKey, value);
|
||||||
|
if (value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const idx = this.importer.assignedTemplateFields.findIndex(
|
||||||
|
(f) => f === fieldKey
|
||||||
|
);
|
||||||
|
|
||||||
|
if (idx >= 0) {
|
||||||
|
this.importer.assignedTemplateFields[idx] = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
showMe(): void {
|
showMe(): void {
|
||||||
const schemaName = this.importer.schemaName;
|
const schemaName = this.importer.schemaName;
|
||||||
this.clear();
|
this.clear();
|
||||||
@ -560,10 +693,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.importType = importType;
|
this.importType = importType;
|
||||||
this.nullOrImporter = new Importer(
|
this.nullOrImporter = new Importer(importType, fyo);
|
||||||
this.labelSchemaNameMap[this.importType],
|
|
||||||
fyo
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
setLoadingStatus(
|
setLoadingStatus(
|
||||||
isMakingEntries: boolean,
|
isMakingEntries: boolean,
|
||||||
@ -617,13 +747,7 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Add Pick Modal
|
* TODO: Show some indication if the value has an error
|
||||||
* TODO: Build Grid Body
|
* TODO: Check if links exist
|
||||||
* TODO: View raw values
|
|
||||||
* TODO: If field not assigned to column show raw value
|
|
||||||
* TODO: If field assigned to column show respective FormControl
|
|
||||||
* TODO: If error in parsing the value, mark as error and call
|
|
||||||
* - for editing value
|
|
||||||
* TODO: View parsed values (after columns have been assigned)
|
|
||||||
*/
|
*/
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
Reference in New Issue
Block a user