Skip to content

Commit

Permalink
Improved: UI for displaying mapping fields on export page, changed th…
Browse files Browse the repository at this point in the history
…e button to fab, added support to reorder fields, and updated the logic to save the mappings(#269)
  • Loading branch information
ymaheshwari1 committed Sep 26, 2023
1 parent 9ab011c commit c7c367b
Showing 1 changed file with 96 additions and 42 deletions.
138 changes: 96 additions & 42 deletions src/views/DownloadPackedOrders.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,41 @@
</div>
</ion-list>

<ion-list>
<!-- TODO: check if we can have button inside header and if yes how to move the button to end -->
<ion-list-header>{{ $t('Selected Fields: ') }} {{ Object.keys(selectedFieldMappings).length }}
<ion-button fill="clear" @click="addCustomField()" :disabled="!Object.keys(fieldMapping).length">{{ $t('Add custom field') }}</ion-button>
</ion-list-header>
<ion-reorder-group @ionItemReorder="doReorder($event)" :disabled="false">
<ion-item :key="field" v-for="(value, field) in selectedFieldMappings">
<ion-icon v-if="!customFields[field]" @click="updateSelectedData(field)" slot="start" color="danger" :icon="removeCircleOutline"/>
<ion-label>{{ fields[field] ? fields[field].label : field }}</ion-label>
<ion-button v-if="!customFields[field] && value === field" fill="outline" @click="addCustomLabel(field)">{{ $t('Custom Label') }}</ion-button>
<!-- Using multiple if's instead of wrapping in a single parent div, to style the component properly without adding any extra css -->
<ion-label v-if="!customFields[field] && value !== field" slot="end">{{ value }}</ion-label>
<ion-button v-if="!customFields[field] && value !== field" slot="end" fill="clear" @click="addCustomLabel(field)">
<ion-icon :icon="pencilOutline" />
</ion-button>
<ion-label v-if="customFields[field]" slot="end">{{ value }}</ion-label>
<ion-button v-if="customFields[field]" slot="end" fill="clear" @click="removeCustomField(field)">
<ion-icon :icon="trashOutline" />
</ion-button>
<ion-reorder slot="end"/>
</ion-item>
</ion-reorder-group>
</ion-list>

<ion-list>
<ion-list-header>{{ $t("Select the fields you want to include in your export") }}</ion-list-header>
<ion-button fill="clear" @click="selectAll" :disabled="!Object.keys(fieldMapping).length">{{ $t('Select all') }}</ion-button>

<ion-item :key="field" v-for="(value, field) in fieldMapping">
<ion-checkbox :checked="selectedData[field]" @click="isFieldClicked=true" @ionChange="updateSelectedData(field)" slot="start"/>
<ion-item :key="field" v-for="(value, field) in fieldMapping" v-show="!selectedData[field]">
<ion-icon @click="updateSelectedData(field)" slot="start" color="success" :icon="addCircleOutline"/>
<ion-label>{{ fields[field] ? fields[field].label : field }}</ion-label>
<ion-button v-if="value === field" fill="outline" @click="addCustomLabel(field)">{{ $t('Custom Label') }}</ion-button>
<!-- Using multiple if's instead of wrapping in a single parent div, to style the component properly without adding any extra css -->
<ion-label v-if="value !== field" slot="end">{{ value }}</ion-label>
<ion-button v-if="value !== field" slot="end" fill="clear" @click="addCustomLabel(field)">
<ion-icon :icon="pencilOutline" />
</ion-button>
</ion-item>
</ion-list>

<ion-list>
<ion-button fill="clear" @click="addCustomField()" :disabled="!Object.keys(fieldMapping).length">{{ $t('Add custom field') }}</ion-button>

<ion-item :key="key" v-for="(field, key) in customFields">
<ion-label>{{ key }}</ion-label>
<ion-label slot="end">{{ field.value }}</ion-label>
Expand All @@ -49,21 +65,22 @@
</ion-button>
</ion-item>
</ion-list>

<ion-button size="large" :disabled="!content.length" color="medium" @click="download" expand="block">
{{ $t("Export") }}
</ion-button>

</main>

<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button @click="download" :disabled="!content.length">
<ion-icon :icon="cloudDownloadOutline" />
</ion-fab-button>
</ion-fab>
</ion-content>
</ion-page>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { mapGetters } from "vuex";
import { alertController, IonBackButton, IonButton, IonCheckbox, IonChip, IonContent, IonHeader, IonIcon, IonItem, IonLabel, IonList, IonListHeader, IonPage, IonTitle, IonToolbar, modalController } from '@ionic/vue'
import { addOutline, pencilOutline, trashOutline } from 'ionicons/icons'
import { alertController, IonBackButton, IonButton, IonChip, IonContent, IonFab, IonFabButton, IonHeader, IonIcon, IonItem, IonLabel, IonList, IonListHeader, IonPage, IonReorder, IonReorderGroup, IonTitle, IonToolbar, modalController } from '@ionic/vue'
import { addCircleOutline, addOutline, cloudDownloadOutline, pencilOutline, removeCircleOutline, trashOutline } from 'ionicons/icons'
import { parseCsv, jsonToCsv, showToast } from '@/utils';
import { translate } from "@/i18n";
import logger from '@/logger';
Expand All @@ -78,16 +95,19 @@ export default defineComponent({
components: {
IonBackButton,
IonButton,
IonCheckbox,
IonChip,
IonContent,
IonFab,
IonFabButton,
IonHeader,
IonIcon,
IonItem,
IonLabel,
IonList,
IonListHeader,
IonPage,
IonReorder,
IonReorderGroup,
IonTitle,
IonToolbar
},
Expand All @@ -97,9 +117,9 @@ export default defineComponent({
fieldMapping: {} as any,
dataColumns: [] as Array<string>,
selectedData: {} as any,
isFieldClicked: false,
fields: process.env["VUE_APP_MAPPING_EXPORD"] ? JSON.parse(process.env["VUE_APP_MAPPING_EXPORD"]) : {},
customFields: {} as any
customFields: {} as any,
selectedFieldMappings: {} as any
}
},
computed: {
Expand Down Expand Up @@ -128,8 +148,6 @@ export default defineComponent({
const resp = await UploadService.fetchPackedOrders(payload);
if(resp.status == 200 && resp.data) {
// generating mapping only when we get the packed orders information
this.generateFieldMapping();
await this.parse(resp.data)
} else {
throw resp.data
Expand All @@ -150,6 +168,8 @@ export default defineComponent({
this.content = await parseCsv(data).then(res => res);
// get the column names from the data
this.dataColumns = Object.keys(this.content[0]);
// generating mapping only when we get the packed orders information and parsing of data is successfull
this.generateFieldMapping();
} catch {
this.content = []
logger.error("Failed to parse the data");
Expand All @@ -175,6 +195,7 @@ export default defineComponent({
this.fieldMapping[field] = value ? value : field;
// selecting the field value when the label for the field is changed
this.selectedData[field] = this.fieldMapping[field]
this.selectedFieldMappings[field] = this.fieldMapping[field]
}
}],
inputs: [{
Expand All @@ -188,14 +209,12 @@ export default defineComponent({
await alert.present();
},
updateSelectedData(field: any) {
// Using isFieldClicked variable as when checked property is changed programatically it calls this method, resulting in wrong behaviour
if(this.isFieldClicked) {
if(this.selectedData[field]) {
delete this.selectedData[field]
} else {
this.selectedData[field] = this.fieldMapping[field]
}
this.isFieldClicked = false;
if(this.selectedData[field]) {
delete this.selectedData[field]
delete this.selectedFieldMappings[field]
} else {
this.selectedData[field] = this.fieldMapping[field]
this.selectedFieldMappings[field] = this.fieldMapping[field]
}
},
async download() {
Expand All @@ -207,17 +226,18 @@ export default defineComponent({
const downloadData = [] as any
this.content.map((order: any) => {
downloadData.push(Object.keys(this.selectedData).reduce((orderInfo: any, property: string) => {
orderInfo[this.selectedData[property]] = order[property]
downloadData.push(Object.keys(this.selectedFieldMappings).reduce((orderInfo: any, property: string) => {
const isCustomField = !order[property]
if(isCustomField) {
orderInfo[property] = this.selectedFieldMappings[property]
} else {
orderInfo[this.selectedFieldMappings[property]] = order[property]
}
return orderInfo
}, {}))
})
// adding custom fields in the data
Object.keys(this.customFields).map((field: any) => {
downloadData.map((data: any) => data[field] = this.customFields[field].value)
})
const alert = await alertController.create({
header: this.$t("Download packed orders"),
message: this.$t("Make sure all the labels provided are correct."),
Expand All @@ -237,22 +257,37 @@ export default defineComponent({
},
selectAll() {
this.selectedData = JSON.parse(JSON.stringify(this.fieldMapping))
this.selectedFieldMappings = JSON.parse(JSON.stringify(this.fieldMapping))
},
async addFieldMapping() {
let mappings: any = {};
Object.keys(this.fieldMapping).map((mapping) => {
// added the support to loop on selectedFieldMappings as to save the order of fields, if changed on reordering
Object.keys(this.selectedFieldMappings).map((mapping) => {
let isSelected = false;
if(this.selectedData[mapping]) {
if(this.customFields[mapping]) {
mappings[mapping] = {
value: this.customFields[mapping].value,
label: mapping,
isSelected: true,
isCustomField: true
}
} else if(this.selectedFieldMappings[mapping]) {
isSelected = true
mappings[mapping] = { value: this.fieldMapping[mapping], isSelected, label: this.fields[mapping].label }
}
})
mappings[mapping] = { value: this.fieldMapping[mapping], isSelected, label: this.fields[mapping].label }
Object.keys(this.fieldMapping).map((mapping) => {
if(!mappings[mapping]) {
mappings[mapping] = { value: this.fieldMapping[mapping], isSelected: false, label: this.fields[mapping].label }
}
})
const createMappingModal = await modalController.create({
component: CreateMappingModal,
componentProps: { content: this.content, mappings: { ...mappings, ...this.customFields }, mappingType: 'EXPORD'}
componentProps: { content: this.content, mappings: { ...mappings }, mappingType: 'EXPORD'}
});
return createMappingModal.present();
},
Expand All @@ -263,6 +298,7 @@ export default defineComponent({
this.fieldMapping = {}
this.customFields = {}
this.selectedData = {}
this.selectedFieldMappings = {}
Object.keys(mappingValue).map((mapping) => {
if(mappingValue[mapping].isCustomField) {
Expand All @@ -272,12 +308,14 @@ export default defineComponent({
isSelected: true,
isCustomField: true
}
this.selectedFieldMappings[mapping] = mappingValue[mapping].value
} else {
this.fieldMapping[mapping] = mappingValue[mapping].value
}
if(mappingValue[mapping].isSelected && !mappingValue[mapping].isCustomField) {
this.selectedData[mapping] = mappingValue[mapping].value
this.selectedFieldMappings[mapping] = mappingValue[mapping].value
}
})
},
Expand All @@ -295,21 +333,37 @@ export default defineComponent({
isSelected: true,
isCustomField: true
}
this.selectedFieldMappings[result.data.value.key] = result.data.value.value
}
})
return customFieldModal.present();
},
removeCustomField(key: any) {
delete this.customFields[key];
}
delete this.selectedFieldMappings[key];
},
doReorder(event: CustomEvent) {
// making the item reorder action as complete
let updatedSeq = event.detail.complete(JSON.parse(JSON.stringify(Object.keys(this.selectedFieldMappings))));
this.selectedFieldMappings = updatedSeq.reduce((updatedData: any, field: string) => {
updatedData[field] = this.selectedFieldMappings[field]
return updatedData
}, {})
},
},
setup() {
const router = useRouter();
return {
addCircleOutline,
addOutline,
cloudDownloadOutline,
pencilOutline,
removeCircleOutline,
trashOutline,
router
}
Expand Down

0 comments on commit c7c367b

Please sign in to comment.