Skip to content

Commit

Permalink
Merge pull request #99 from k2maan/#2hran7u-updated
Browse files Browse the repository at this point in the history
Implemented logic for storing and applying multiple field mappings (#2hran7u)
  • Loading branch information
adityasharma7 authored Nov 22, 2022
2 parents ba9161a + 835c764 commit 7e80bd0
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 26 deletions.
4 changes: 2 additions & 2 deletions src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ const i18n = createI18n({

// TODO Check if this is needed in updated versions
// Currently this method is added to be used in ts files
const translate = (key: string) => {
const translate = (key: string, named?: Record<string, unknown>) => {
if (!key) {
return '';
}
return i18n.global.t(key);
return named ? i18n.global.t(key, named) : i18n.global.t(key);
};

export { i18n as default, translate }
5 changes: 5 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"Confirm": "Confirm",
"Date Format": "Date Format",
"Dismiss": "Dismiss",
"Enter mapping name": "Enter mapping name",
"Enter a custom date time format that you want to use when uploading documents to HotWax Commerce.": "Enter a custom date time format that you want to use when uploading documents to HotWax Commerce.",
"Facility": "Facility",
"Facility ID": "Facility ID",
Expand All @@ -29,6 +30,7 @@
"Leave page": "Leave page",
"Login": "Login",
"Logout": "Logout",
"Mapping name": "Mapping name",
"Luxon date time formats can be found": "Luxon date time formats can be found",
"Missing SKUs": "Missing SKUs",
"No time zone found": "No time zone found",
Expand All @@ -50,14 +52,17 @@
"Reset": "Reset",
"Reset password": "Reset password",
"Safety stock": "Safety stock",
"Save mapping": "Save mapping",
"Sample": "Sample",
"Save": "Save",
"Search products": "Search products",
"Search time zones": "Search time zones",
"Select": "Select",
"Select mapping": "Select mapping",
"Select all the fields to continue": "Select all the fields to continue",
"Select time zone": "Select time zone",
"Select the column index for the following information in the uploaded CSV.": "Select the column index for the following information in the uploaded CSV.",
"Some of the mapping fields are missing in the CSV: ": "Some of the mapping fields are missing in the CSV: {missingFields}",
"Something went wrong": "Something went wrong",
"Something went wrong, Please try again": "Something went wrong, Please try again",
"Sorry, your username or password is incorrect. Please try again.": "Sorry, your username or password is incorrect. Please try again.",
Expand Down
1 change: 1 addition & 0 deletions src/store/modules/user/UserState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ export default interface UserState {
current: object | null;
currentFacility: object;
instanceUrl: string;
fieldMappings: any;
preferredDateTimeFormat: string;
}
4 changes: 4 additions & 0 deletions src/store/modules/user/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ const actions: ActionTree<UserState, RootState> = {
*/
setUserInstanceUrl ({ commit }, payload){
commit(types.USER_INSTANCE_URL_UPDATED, payload)
},

updateFieldMappings({ commit }, payload){
commit(types.USER_FIELD_MAPPINGS_UPDATED, payload);
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/store/modules/user/getters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ const getters: GetterTree <UserState, RootState> = {
},
getInstanceUrl (state) {
const baseUrl = process.env.VUE_APP_BASE_URL;
return baseUrl ? baseUrl : state.instanceUrl;
return baseUrl ? baseUrl : state.instanceUrl;
},
getFieldMappings(state) {
return state.fieldMappings;
},
getDateTimeFormat (state) {
return state.preferredDateTimeFormat;
Expand Down
1 change: 1 addition & 0 deletions src/store/modules/user/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const userModule: Module<UserState, RootState> = {
current: null,
currentFacility: {},
instanceUrl: '',
fieldMappings: {},
preferredDateTimeFormat: ''
},
getters,
Expand Down
3 changes: 2 additions & 1 deletion src/store/modules/user/mutation-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export const USER_END_SESSION = SN_USER + '/END_SESSION'
export const USER_INFO_UPDATED = SN_USER + '/INFO_UPDATED'
export const USER_CURRENT_FACILITY_UPDATED = SN_USER + '/CURRENT_FACILITY_UPDATED'
export const USER_INSTANCE_URL_UPDATED = SN_USER + '/INSTANCE_URL_UPDATED'
export const USER_DATETIME_FORMAT_UPDATED = SN_USER + '/DATETIME_FORMAT_UPDATED'
export const USER_FIELD_MAPPINGS_UPDATED = SN_USER + '/FIELD_MAPPINGS_UPDATED'
export const USER_DATETIME_FORMAT_UPDATED = SN_USER + '/DATETIME_FORMAT_UPDATED'
3 changes: 3 additions & 0 deletions src/store/modules/user/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ const mutations: MutationTree <UserState> = {
[types.USER_INSTANCE_URL_UPDATED] (state, payload) {
state.instanceUrl = payload;
},
[types.USER_FIELD_MAPPINGS_UPDATED] (state, payload) {
state.fieldMappings[payload.mappingPrefId] = payload;
},
[types.USER_DATETIME_FORMAT_UPDATED] (state, payload) {
state.preferredDateTimeFormat = payload;
}
Expand Down
4 changes: 4 additions & 0 deletions src/theme/variables.css
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,10 @@ body {
justify-self: start
}

.toast-message {
--white-space: pre-wrap !important;
}

@media (min-width: 700px) {
.list-item {
--col-calc: var(--columns-tablet);
Expand Down
96 changes: 74 additions & 22 deletions src/views/PurchaseOrder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,65 +10,80 @@
<ion-content>
<main>
<ion-item>
<ion-label>{{ file.name ? $t("Purchase order ") + file.name : $t('Purchase order') }}</ion-label>
<ion-label>{{ file.name ? $t("Purchase order") + file.name : $t('Purchase order') }}</ion-label>
<input @change="getFile" ref="file" class="ion-hide" type="file" id="inputFile"/>
<label for="inputFile">{{ $t("Upload") }}</label>
</ion-item>
</ion-item>
<ion-item lines="none">
<ion-label>{{ $t("Select mapping") }}</ion-label>
<ion-select :disabled="!Object.keys(fieldMappings).length" interface="popover" @ionChange="mapFields">
<ion-select-option v-for="mapping in fieldMappings" :value="mapping" :key="mapping?.mappingPrefId">{{ mapping?.mappingPrefName }}</ion-select-option>
</ion-select>
</ion-item>

<ion-list>
<ion-list-header>{{ $t("Select the column index for the following information in the uploaded CSV.") }}</ion-list-header>

<ion-item>
<ion-label>{{ $t("Order ID") }}</ion-label>
<ion-select interface="popover" v-if="content.length" :placeholder = "$t('Select')" v-model="fields.orderId">
<ion-select-option :key="index" v-for="(prop, index) in Object.keys(content[0])">{{ prop }}</ion-select-option>
<ion-select interface="popover" v-if="content.length" :placeholder = "$t('Select')" v-model="fieldMapping.orderId">
<ion-select-option :key="index" v-for="(prop, index) in Object.keys(content[0])">{{ prop }}</ion-select-option>
</ion-select>
</ion-item>

<ion-item>
<ion-label>{{ $t("Shopify product SKU") }}</ion-label>
<ion-select interface="popover" v-if="content.length" :placeholder = "$t('Select')" v-model="fields.productSku">
<ion-select interface="popover" v-if="content.length" :placeholder = "$t('Select')" v-model="fieldMapping.productSku">
<ion-select-option :key="index" v-for="(prop, index) in Object.keys(content[0])">{{ prop }}</ion-select-option>
</ion-select>
</ion-item>

<ion-item>
<ion-label>{{ $t("Arrival date") }}</ion-label>
<ion-select interface="popover" v-if="content.length" :placeholder = "$t('Select')" v-model="fields.date">
<ion-select interface="popover" v-if="content.length" :placeholder = "$t('Select')" v-model="fieldMapping.orderDate">
<ion-select-option :key="index" v-for="(prop, index) in Object.keys(content[0])">{{ prop }}</ion-select-option>
</ion-select>
</ion-item>

<ion-item>
<ion-label>{{ $t("Ordered quantity") }}</ion-label>
<ion-select interface="popover" v-if="content.length" :placeholder = "$t('Select')" v-model="fields.quantity">
<ion-select interface="popover" v-if="content.length" :placeholder = "$t('Select')" v-model="fieldMapping.quantity">
<ion-select-option :key="index" v-for="(prop, index) in Object.keys(content[0])">{{ prop }}</ion-select-option>
</ion-select>
</ion-item>

<ion-item>
<ion-label>{{ $t("Facility ID") }}</ion-label>
<ion-select interface="popover" v-if="content.length" :placeholder = "$t('Select')" v-model="fields.facility">
<ion-select interface="popover" v-if="content.length" :placeholder = "$t('Select')" v-model="fieldMapping.facility">
<ion-select-option :key="index" v-for="(prop, index) in Object.keys(content[0])">{{ prop }}</ion-select-option>
</ion-select>
</ion-item>
</ion-list>

<ion-button color="medium" @click="mapFields" expand="block">
<ion-button color="medium" @click="review" expand="block">
{{ $t("Review") }}
<ion-icon slot="end" :icon="arrowForwardOutline" />
</ion-button>

<ion-item>
<ion-label>{{ $t("Mapping name") }}</ion-label>
<ion-input v-model="mappingName" />
</ion-item>
<ion-button @click="saveMapping">{{ $t("Save mapping") }}</ion-button>

</main>
</ion-content>
</ion-page>
</template>
<script>
import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonItem, IonLabel, IonList, IonListHeader, IonMenuButton, IonButton, IonSelect, IonSelectOption, IonIcon } from "@ionic/vue";
import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonItem, IonInput, IonLabel, IonList, IonListHeader, IonMenuButton, IonButton, IonSelect, IonSelectOption, IonIcon } from "@ionic/vue";
import { defineComponent } from "vue";
import { useRouter } from 'vue-router';
import { useStore, mapGetters } from "vuex";
import { showToast, parseCsv } from '@/utils';
import { translate } from "@/i18n";
import { arrowForwardOutline } from 'ionicons/icons';
import { DateTime } from 'luxon';
export default defineComponent({
name: "purchase orders",
Expand All @@ -79,6 +94,7 @@ export default defineComponent({
IonTitle,
IonContent,
IonItem,
IonInput,
IonLabel,
IonButton,
IonMenuButton,
Expand All @@ -91,23 +107,38 @@ export default defineComponent({
computed: {
...mapGetters({
dateTimeFormat : 'user/getDateTimeFormat',
fieldMappings: 'user/getFieldMappings'
})
},
data() {
return {
file: "",
content: [],
fields:{
fieldMapping: {
orderId: "",
productSku: "",
date: "",
orderDate: "",
quantity: "",
facility: "",
},
mappingName: "",
orderItemsList: [],
}
},
methods: {
//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() {
const id = Math.floor(Math.random() * 1000);
return !this.fieldMappings[id] ? id : this.generateUniqueMappingPrefId();
},
saveMapping() {
if (this.mappingName) {
const mappingPrefId = this.generateUniqueMappingPrefId();
this.store.dispatch('user/updateFieldMappings', { mappingPrefId, mappingPrefName: this.mappingName, mappingPrefValue: JSON.parse(JSON.stringify(this.fieldMapping)) })
} else {
showToast(translate("Enter mapping name"));
}
},
getFile(event) {
this.file = event.target.files[0];
if(this.file){
Expand All @@ -119,30 +150,51 @@ export default defineComponent({
showToast(translate("Something went wrong, Please try again"));
}
},
async parseFile(){
await parseCsv(this.file).then(res => {
this.content = res;
})
},
mapFields() {
const areAllFieldsSelected = Object.values(this.fields).every(field => field !== "");
review() {
const areAllFieldsSelected = Object.values(this.fieldMapping).every(field => field !== "");
if (this.content.length <= 0) {
showToast(translate("Please upload a valid purchase order csv to continue"));
} else if (areAllFieldsSelected) {
this.orderItemsList = this.content.map(item => ({
orderId: item[this.fields.orderId],
shopifyProductSKU: item[this.fields.productSku],
arrivalDate: item[this.fields.date],
quantityOrdered: item[this.fields.quantity],
externalFacilityId: item[this.fields.facility]
}))
this.orderItemsList = this.content.map(item => {
return {
orderId: item[this.fieldMapping.orderId],
shopifyProductSKU: item[this.fieldMapping.productSku],
arrivalDate: DateTime.fromFormat(item[this.fieldMapping.orderDate], this.dateTimeFormat).toFormat(this.dateTimeFormat), //This is to verify whether the date format is correct.
quantityOrdered: item[this.fieldMapping.quantity],
facilityId: '',
externalFacilityId: item[this.fieldMapping.facility]
}
})
this.store.dispatch('order/updatedOrderList', this.orderItemsList);
this.router.push({
name:'PurchaseOrderDetail'
})
} else {
showToast(translate("Select all the fields to continue"));
}
},
mapFields(event) {
if(event && event.detail.value) {
const fieldMapping = JSON.parse(JSON.stringify(event.detail.value));
const CsvFields = Object.keys(this.content[0]);
const missingFields = Object.values(fieldMapping.mappingPrefValue).filter(field => {
if(!Object.keys(this.content[0]).includes(field)) return field;
});
if(missingFields.length) showToast(translate("Some of the mapping fields are missing in the CSV: ", { missingFields: missingFields.join(", ") }))
Object.keys(fieldMapping.mappingPrefValue).map((field) => {
if(!CsvFields.includes(fieldMapping.mappingPrefValue[field])){
fieldMapping.mappingPrefValue[field] = "";
}
})
this.fieldMapping = fieldMapping.mappingPrefValue;
this.mappingName = fieldMapping.mappingPrefName;
}
},
},
Expand Down

0 comments on commit 7e80bd0

Please sign in to comment.