Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented: support to save mappings and apply already available mappings(#192) #204

Merged
merged 27 commits into from
Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
eb1d82d
Implemented: router and view to handle saved mappings(#192)
ymaheshwari1 Jul 17, 2023
2668458
Added: support to create new mapping and apply already created mappin…
ymaheshwari1 Jul 17, 2023
0f8ee03
Implemented: support to update already saved mapping and display the …
ymaheshwari1 Jul 17, 2023
9aa0c1d
Updated: locales file for static text(#192)
ymaheshwari1 Jul 17, 2023
04e0f01
Implemented: support for saving the export mappings(#192)
ymaheshwari1 Jul 18, 2023
53b1b77
Updated: locale file and added import for chip component(#192)
ymaheshwari1 Jul 18, 2023
63d4553
Merge branch 'main' into fulfillment/#192
ymaheshwari1 Jul 18, 2023
e22f3a5
Removed: some logic for mapping fields, and handled the case when cre…
ymaheshwari1 Jul 18, 2023
055e982
Merge branch 'main' into fulfillment/#192
ymaheshwari1 Jul 19, 2023
52428b4
Improved: logic ot prepare field mappings after fetching the packed o…
ymaheshwari1 Jul 19, 2023
722c5b0
Added: support to check for already available custom label and displa…
ymaheshwari1 Jul 19, 2023
82660ce
Updated: variable name for adding box feature(#192)
ymaheshwari1 Jul 19, 2023
13ecfb8
Updated: locale files for static text(#192)
ymaheshwari1 Jul 19, 2023
512dc29
Updated: locales file(#192)
ymaheshwari1 Jul 19, 2023
5310a7d
Removed: mapping option from menu and updated the UI for saved mappin…
ymaheshwari1 Jul 19, 2023
e77071d
Improved: support to select a field by default when label is changed …
ymaheshwari1 Jul 20, 2023
89459dd
Implemented: support to select a field when applying an already avail…
ymaheshwari1 Jul 20, 2023
f6aa23e
Updated: locale files, replaced menu button with back button and upda…
ymaheshwari1 Jul 20, 2023
0ae8950
Improved: variable names for mappings(#192)
ymaheshwari1 Jul 20, 2023
f880240
Updated: code to for saving and accessing mappings(#192)
ymaheshwari1 Jul 21, 2023
9e63ded
Updated: the structure for mappings in the env example file(#192)
ymaheshwari1 Jul 21, 2023
509a6bc
Fixed: issue of fields mappings on the import page(#192)
ymaheshwari1 Jul 21, 2023
7533dec
Fixed: code when applying a mapping on download page to show the cust…
ymaheshwari1 Jul 21, 2023
70ab2a3
Fixed: issue of custom field value also getting added as a column in …
ymaheshwari1 Jul 21, 2023
3cbe70a
Fixed: downloaded csv column being undefined(#192)
ymaheshwari1 Jul 24, 2023
d3335b4
Merge branch 'main' into fulfillment/#192
ymaheshwari1 Jul 25, 2023
3e40357
Removed: isSelected property from env file(#192)
ymaheshwari1 Jul 25, 2023
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
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ VUE_APP_BASE_URL=
VUE_APP_PERMISSION_ID=
VUE_APP_ALIAS={}
VUE_APP_DEFAULT_LOG_LEVEL="error"
VUE_APP_UPLD_IMP_ORD={"orderId": { "label": "Order ID", "required": true }, "facilityId": { "label": "Facility ID", "required": true },"trackingCode": { "label": "Tracking Code", "required": true }}
VUE_APP_MAPPING_TYPES={"IMPORD": "IMP_ORD_MAPPING_PREF","EXPORD": "EXP_PKD_ORD_MAPPING_PREF"}
VUE_APP_MAPPING_IMPORD={"orderId": { "label": "Order ID", "value": "" }, "facilityId": { "label": "Facility ID", "value": "" },"trackingCode": { "label": "Tracking Code", "value": "" }}
VUE_APP_MAPPING_EXPORD={"shipment-id": { "label": "Shipment ID", "value": "" }, "order-id": { "label": "Order ID", "value": "" },"to-name": { "label": "To Name", "value": "" },"address1": { "label": "Address 1", "value": "" },"address2": { "label": "Address 2", "value": "" },"city": { "label": "City", "value": "" },"state": { "label": "State", "value": "" },"zip-code": { "label": "Zip Code", "value": "" },"country-code": { "label": "Country Code", "value": "" },"full-to-address": { "label": "Full Address", "value": "" },"phone-number": { "label": "Phone Number", "value": "" },"email-address": { "label": "Email", "value": "" },"weight": { "label": "Weight", "value": "" },"quantity": { "label": "Quantity", "value": "" },"product-name": { "label": "Product Name", "value": "" },"product-sku": { "label": "Product Sku", "value": "" },"shipping-method": { "label": "Shipping Method", "value": "" },"facility-name": { "label": "Facility Name", "value": "" },"facility-address1": { "label": "Facility Address 1", "value": "" },"facility-address2": { "label": "Facility Address 2", "value": "" },"facility-city": { "label": "Facility City", "value": "" },"facility-state": { "label": "Facility State", "value": "" },"facility-zip-code": { "label": "Facility Zip Code", "value": "" },"facility-phone": { "label": "Facility Phone", "value": "" },"facility-full-address": { "label": "Facility Full Address", "value": "" }}
27 changes: 26 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</template>

<script lang="ts">
import { IonApp, IonRouterOutlet, IonSplitPane } from '@ionic/vue';
import { createAnimation, IonApp, IonRouterOutlet, IonSplitPane } from '@ionic/vue';
import { defineComponent } from 'vue';
import Menu from '@/components/Menu.vue';
import { loadingController } from '@ionic/vue';
Expand Down Expand Up @@ -60,6 +60,29 @@ export default defineComponent({
async unauthorised() {
this.store.dispatch("user/logout");
this.router.push("/login")
},
playAnimation() {
const aside = document.querySelector('aside') as Element
const main = document.querySelector('main') as Element

const revealAnimation = createAnimation()
.addElement(aside)
.duration(1500)
.easing('ease')
.keyframes([
{ offset: 0, flex: '0', opacity: '0' },
{ offset: 0.5, flex: '1', opacity: '0' },
{ offset: 1, flex: '1', opacity: '1' }
])

const gapAnimation = createAnimation()
.addElement(main)
.duration(500)
.fromTo('gap', '0', 'var(--spacer-2xl)');

createAnimation()
.addAnimation([gapAnimation, revealAnimation])
.play();
}
},
created() {
Expand Down Expand Up @@ -87,6 +110,7 @@ export default defineComponent({
});
emitter.on('presentLoader', this.presentLoader);
emitter.on('dismissLoader', this.dismissLoader);
emitter.on('playAnimation', this.playAnimation);

// Handles case when user resumes or reloads the app
// Luxon timezzone should be set with the user's selected timezone
Expand All @@ -97,6 +121,7 @@ export default defineComponent({
unmounted() {
emitter.off('presentLoader', this.presentLoader);
emitter.off('dismissLoader', this.dismissLoader);
emitter.off('playAnimation', this.playAnimation);
resetConfig()
},
setup() {
Expand Down
143 changes: 143 additions & 0 deletions src/components/CreateMappingModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<template>
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button @click="closeModal">
<ion-icon :icon="close" />
</ion-button>
</ion-buttons>
<ion-title>{{ $t("CSV Mapping") }}</ion-title>
</ion-toolbar>
</ion-header>

<ion-item>
<ion-label>{{ $t("Mapping name") }}</ion-label>
<ion-input :placeholder="$t('Field mapping name')" v-model="mappingName" />
</ion-item>

<ion-content class="ion-padding">
<div>
<ion-list>
<ion-item :key="field" v-for="(fieldValues, field) in fieldMapping">
<ion-label>{{ $t(fieldValues.label) }}</ion-label>
<ion-input v-if="mappingType === 'EXPORD'" slot="end" v-model="fieldValues.value"></ion-input>
<ion-select v-else interface="popover" :placeholder = "$t('Select')" v-model="fieldValues.value">
<ion-select-option :key="index" v-for="(prop, index) in fileColumns">{{ prop }}</ion-select-option>
</ion-select>
</ion-item>
</ion-list>
</div>
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button @click="saveMapping">
<ion-icon :icon="saveOutline" />
</ion-fab-button>
</ion-fab>
</ion-content>
</template>

<script lang="ts">
import {
IonButtons,
IonButton,
IonContent,
IonHeader,
IonIcon,
IonFab,
IonFabButton,
IonInput,
IonTitle,
IonToolbar,
IonLabel,
IonItem,
IonList,
IonSelect,
IonSelectOption,
modalController
} from "@ionic/vue";
import { defineComponent } from "vue";
import { close, save, saveOutline } from "ionicons/icons";
import { useStore, mapGetters } from "vuex";
import { showToast } from '@/utils';
import { translate } from "@/i18n";

export default defineComponent({
name: "CreateMappingModal",
components: {
IonButtons,
IonButton,
IonContent,
IonFab,
IonFabButton,
IonHeader,
IonIcon,
IonInput,
IonSelect,
IonSelectOption,
IonTitle,
IonToolbar,
IonLabel,
IonItem,
IonList
},
data() {
return {
mappingName: "",
fieldMapping: {} as any,
fileColumns: [] as any
}
},
props: ["content", "mappings", "mappingType"],
mounted() {
// mappings needs to be in format { <key>: { value: <value>, label: <label>, isSelected | optional: <boolean> }}
this.fieldMapping = JSON.parse(JSON.stringify(this.mappings));
this.fileColumns = Object.keys(this.content[0]);
},
computed: {
...mapGetters({
fieldMappings: 'user/getFieldMappings'
})
},
methods: {
closeModal() {
modalController.dismiss({ dismissed: true });
},
async saveMapping() {
if(!this.mappingName) {
showToast(translate("Enter mapping name"));
return
}
if (!this.areAllFieldsSelected()) {
showToast(translate("Map all fields"));
return
}
const id = this.generateUniqueMappingPrefId();

// removing label from mappings as we don't need to save label with the mappings as it will just increase the size of the value
Object.keys(this.fieldMapping).map((mapping) => {
this.fieldMapping[mapping] && delete this.fieldMapping[mapping].label
return;
})

await this.store.dispatch("user/createFieldMapping", { id, name: this.mappingName, value: this.fieldMapping, mappingType: this.mappingType })
this.closeModal();
},
areAllFieldsSelected() {
return Object.values(this.fieldMapping).every((field: any) => field.value !== "");
},
//Todo: Generating unique identifiers as we are currently storing in local storage. Need to remove it as we will be storing data on server.
generateUniqueMappingPrefId(): any {
const id = Math.floor(Math.random() * 1000);
return !this.fieldMappings[id] ? id : this.generateUniqueMappingPrefId();
}
},
setup() {
const store = useStore();
return {
close,
save,
saveOutline,
store
};
}
});
</script>
7 changes: 5 additions & 2 deletions src/components/CustomFieldModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,17 @@ export default defineComponent({
value: ''
}
},
props: ["customFields"],
methods: {
closeModal() {
modalController.dismiss({ dismissed: true});
},
saveCustomField() {
const fieldKey = this.key.trim();
if(!fieldKey) {
showToast(translate('Please enter a valid key'))
const errorMessage = fieldKey ? this.customFields[fieldKey] ? 'Please enter a unique key' : '' : 'Please enter a valid key'

if(errorMessage) {
showToast(translate(errorMessage))
return;
}
modalController.dismiss({ dismissed: true, value: { key: fieldKey, value: this.value } });
Expand Down
142 changes: 142 additions & 0 deletions src/components/MappingConfiguration.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<template>
<section>
<ion-item>
<ion-label>{{ $t("Mapping name") }}</ion-label>
<ion-input v-model="currentMapping.name" />
</ion-item>

<ion-list>
<ion-item :key="field" v-for="(fieldValues, field) in currentMapping.value">
<ion-label>{{ fields[field] ? fields[field].label : field }}</ion-label>
<ion-input v-model="fieldValues.value" />
</ion-item>
</ion-list>

<div class="ion-padding-top actions desktop-only">
<div>
<ion-button size="small" @click="updateMapping()">
<ion-icon slot="start" :icon="saveOutline"/>
{{ $t("Save Changes") }}
</ion-button>
<ion-button size="small" fill="outline" color="danger" @click="deleteMapping()">
<ion-icon slot="start" :icon="trashOutline" />
{{ $t("Delete mapping") }}
</ion-button>
</div>
</div>

<div class="ion-padding-top actions mobile-only">
<ion-button expand="block" @click="updateMapping()">
<ion-icon slot="start" :icon="saveOutline"/>
{{ $t("Save Changes") }}
</ion-button>
<ion-button fill="outline" color="danger" expand="block" @click="deleteMapping()">
<ion-icon slot="start" :icon="trashOutline" />
{{ $t("Delete mapping") }}
</ion-button>
</div>
</section>
</template>

<script lang="ts">
import {
alertController,
IonButton,
IonIcon,
IonInput,
IonLabel,
IonItem,
IonList
} from "@ionic/vue";
import { defineComponent } from "vue";
import { close, save, saveOutline, trashOutline } from "ionicons/icons";
import { useStore, mapGetters } from "vuex";
import { showToast } from "@/utils";
import { translate } from "@/i18n";

export default defineComponent({
name: "MappingConfiguration",
components: {
IonButton,
IonIcon,
IonInput,
IonLabel,
IonItem,
IonList
},
computed: {
...mapGetters({
currentMapping: 'user/getCurrentMapping'
})
},
data() {
return {
fields: {} as any
}
},
mounted() {
const fields = process.env["VUE_APP_MAPPING_" + this.currentMapping.mappingType];
this.fields = fields ? JSON.parse(fields) : {};
},
methods: {
async deleteMapping() {
const message = this.$t("Are you sure you want to delete this CSV mapping? This action cannot be undone.");
const alert = await alertController.create({
header: this.$t("Delete mapping"),
message,
buttons: [
{
text: this.$t("Cancel"),
},
{
text: this.$t("Delete"),
handler: () => {
this.store.dispatch("user/deleteFieldMapping", this.currentMapping)}
}
],
});
return alert.present();
},
areAllFieldsSelected() {
return Object.values(this.currentMapping.value).every((field: any) => field.value !== "");
},
async updateMapping() {
if(!this.currentMapping.name) {
showToast(translate("Enter mapping name"));
return;
}
if (!this.areAllFieldsSelected()) {
showToast(translate("Map all fields"));
return;
}
const message = this.$t("Are you sure you want to update this CSV mapping? This action cannot be undone.");
const alert = await alertController.create({
header: this.$t("Update mapping"),
message,
buttons: [
{
text: this.$t("Cancel"),
},
{
text: this.$t("Update"),
handler: () => {
this.store.dispatch('user/updateFieldMapping', this.currentMapping)
}
}
],
});
return alert.present();
}
},
setup() {
const store = useStore();
return {
close,
save,
saveOutline,
trashOutline,
store
};
}
});
</script>
2 changes: 1 addition & 1 deletion src/components/Menu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export default defineComponent({
url: "/exim",
iosIcon: swapVerticalOutline,
mdIcon: swapVerticalOutline,
childRoutes: ["/download-packed-orders", "/upload-import-orders"] // defined child routes as to enable the correct menu when we are on a route that is not listed in the menu
childRoutes: ["/download-packed-orders", "/upload-import-orders", "/saved-mappings"] // defined child routes as to enable the correct menu when we are on a route that is not listed in the menu
},
{
title: "Settings",
Expand Down
Loading
Loading