Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
52 changes: 52 additions & 0 deletions src/components/AddNewSessionModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<template>
<ion-modal :is-open="isOpen" @did-dismiss="closeModal">
<ion-header>
<ion-toolbar>
<ion-title>New Session</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button @click="addNewSession">
<ion-icon :icon="saveOutline"/>
</ion-fab-button>
</ion-fab>
</ion-content>
</ion-modal>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { IonModal, IonIcon, IonFab, IonFabButton, IonContent } from '@ionic/vue';
import { saveOutline } from "ionicons/icons";
import { CountService } from '@/services/CountService';
export default defineComponent({
name: 'AddNewSessionModal',
components: { IonContent, IonModal, IonIcon, IonFab, IonFabButton },
props: {
isOpen: {
type: Boolean,
required: true
}
},
emits: ['update:isOpen'],
setup(props, { emit }) {
const closeModal = () => emit('update:isOpen', false);
function addNewSession() {
CountService.addSessionInCount({
countImportName: "Test Count From Modal",
statusId: "CYCLE_CNT_CREATED",
uploadedByUserLogin: "hotwax.user",
facilityAreaId: "Basement Vault 786",
createdDate: Date.now(),
dueDate: Date.now(),
workEffortId: "M100877"
});
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The addNewSession function uses a hardcoded payload to create a new session. This is not suitable for production and appears to be for demonstration purposes. The data for the new session, particularly the workEffortId, should be passed into this component as a prop. Other details should be collected from the user via form inputs within the modal, which is currently empty.

return { closeModal, saveOutline, addNewSession };
}
});
</script>
15 changes: 12 additions & 3 deletions src/services/CountService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,22 @@ const getAssignedWorkEfforts = async (params: any): Promise <any> => {
}
const getInventoryCountImportsByWorkEffort = async (params: any): Promise <any> => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The params and return types are any. Using specific types would improve type safety and code maintainability. For example, params could be typed as { workEffortId: string }. Please also define an interface for the response object.

return api({
url: `inventory-cycle-count/cycleCounts/workEfforts/${params.workEffortId}/imports`,
url: `inventory-cycle-count/cycleCounts/workEfforts/${params.workEffortId}/sessions`,
method: "get",
params
});
}

const addSessionInCount = async (payload: any): Promise<any> => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The payload and return types are any. Using specific types would improve type safety and code maintainability. Please define an interface for the payload (e.g., AddSessionPayload) and use it here. Also, consider typing the response.

return api({
url: `inventory-cycle-count/cycleCounts/workEfforts/${payload.workEffortId}/sessions`,
method: "post",
data: payload
}
);
}

export const CountService = {
getAssignedWorkEfforts,
getInventoryCountImportsByWorkEffort
getInventoryCountImportsByWorkEffort,
addSessionInCount
}
4 changes: 2 additions & 2 deletions src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import getters from "./getters";
import actions from "./actions";
import userModule from "./modules/user";
// import productModule from "./modules/product";
// import countModule from "./modules/count";
import countModule from "./modules/count";
import utilModule from "./modules/util";
import { setPermissions } from "@/authorization"

Expand All @@ -32,7 +32,7 @@ const store = createStore<RootState>({
modules: {
user: userModule,
// product: productModule,
// count: countModule,
count: countModule,
util: utilModule,
},
})
Expand Down
4 changes: 2 additions & 2 deletions src/store/modules/count/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ const actions: ActionTree<CountState, RootState> = {
if (!hasError(inventoryResp)) {
assignedWorkEfforts.push({
...workEffort,
inventoryCountImports: inventoryResp.data || []
sessions: inventoryResp.data || []
})
}
} catch (err) {
logger.error(`Error fetching inventory imports for workEffortId ${workEffort.workEffortId}:`, err)
}
}

console.log("These are counts: ", assignedWorkEfforts);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This console.log statement appears to be for debugging. It should be removed before merging into the main branch.

total = assignedWorkEfforts.length
isScrollable = workEfforts.length >= params.pageSize
} else {
Expand Down
110 changes: 80 additions & 30 deletions src/views/Count.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
created date
</ion-card-subtitle>
</div>
<ion-note>number of items</ion-note>
</ion-card-header>
<ion-item lines="none">
{{ translate("Due date") }}
Expand All @@ -44,7 +43,7 @@
Sessions
</ion-label>
</ion-list-header>
<ion-button expand="block" class="ion-margin-horizontal">
<ion-button expand="block" class="ion-margin-horizontal" @click="showAddNewSessionModal">
<ion-label>
Start new session
</ion-label>
Expand All @@ -66,7 +65,6 @@
created date
</ion-card-subtitle>
</div>
<ion-note>number of items</ion-note>
</ion-card-header>
<ion-item lines="none">
{{ translate("Due date") }}
Expand All @@ -79,7 +77,7 @@
<ion-label>
Sessions
</ion-label>
<ion-button fill="clear" size="small">
<ion-button fill="clear" size="small" @click="showAddNewSessionModal">
<ion-icon slot="start" :icon="addCircleOutline"></ion-icon>
New
</ion-button>
Expand Down Expand Up @@ -131,7 +129,6 @@
created date
</ion-card-subtitle>
</div>
<ion-note>number of items</ion-note>
</ion-card-header>
<ion-item lines="none">
{{ translate("Due date") }}
Expand Down Expand Up @@ -184,28 +181,57 @@
</ion-list>
</ion-card>

<ion-card v-for="count in cycleCount" :key="count.inventoryCountImportId" @click="navigateToStoreView(count)" button>
<ion-card v-for="count in cycleCount" :key="count.inventoryCountImportId">

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The :key for the v-for loop is count.inventoryCountImportId. However, the count object is a "work effort" from getAssignedWorkEfforts and may not have this property. A more appropriate and stable key would be count.workEffortId. Using an incorrect or non-unique key can lead to unpredictable rendering behavior in Vue.

        <ion-card v-for="count in cycleCount" :key="count.workEffortId">

<ion-card-header>
<div>
<ion-label v-if="count.countTypeEnumId === 'HARD_COUNT'" color="warning" class="overline">
<ion-label v-if="count.workEffortPurposeTypeId === 'HARD_COUNT'" color="warning" class="overline">
{{ translate("HARD COUNT") }}
</ion-label>
<ion-card-title>
{{ count.countImportName }}
{{ count.workEffortName }}
</ion-card-title>
<ion-card-subtitle>
{{ getDateWithOrdinalSuffix(count.createdDate) }}
</ion-card-subtitle>
</div>
<ion-note>{{ cycleCountStats(count.inventoryCountImportId)?.totalItems }} {{ translate("items") }}</ion-note>
</ion-card-header>
<ion-item lines="none">
{{ translate("Due date") }}
<ion-label slot="end">
<p>{{ getDateWithOrdinalSuffix(count.dueDate) }}</p>
</ion-label>
</ion-item>
<ion-list>
<ion-list-header>
<ion-label>
Sessions
</ion-label>
<ion-button v-if="count.sessions?.length" fill="clear" size="small" @click="showAddNewSessionModal">
<ion-icon slot="start" :icon="addCircleOutline"></ion-icon>
New
</ion-button>
</ion-list-header>
<ion-button v-if="count.sessions?.length === 0" expand="block" class="ion-margin-horizontal" @click="showAddNewSessionModal">
<ion-label>
Start new session
</ion-label>
</ion-button>
<ion-item-group v-for="session in count.sessions" :key="session.inventoryCountImportId">
<ion-item>
<ion-label>
{{ session.countImportName }} + {{ session.facilityAreaId }}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The concatenation {{ session.countImportName }} + {{ session.facilityAreaId }} seems like placeholder text for development. This should be formatted in a more user-friendly way for the final UI.

                  {{ session.countImportName }} ({{ session.facilityAreaId }})

<p>
created By {{ session.uploadedByUserLogin }}
</p>
</ion-label>
<ion-note slot="end">
{{ getSessionStatusDescription(session.statusId) }}
</ion-note>
</ion-item>
</ion-item-group>
</ion-list>
</ion-card>
<AddNewSessionModal v-model:isOpen="isAddSessionModalOpen"/>
</template>
<!-- <template v-else>
<p class="empty-state">{{ translate("No cycle counts found") }}</p>
Expand Down Expand Up @@ -245,8 +271,9 @@ import { translate } from '@/i18n';
import { computed, ref } from "vue";
import { useStore } from 'vuex';
import { useRouter } from 'vue-router'
import { getCycleCountStats, getDateWithOrdinalSuffix, showToast } from "@/utils"
import { getDateWithOrdinalSuffix, showToast } from "@/utils"
import { useUserStore } from '@hotwax/dxp-components';
import AddNewSessionModal from '@/components/AddNewSessionModal.vue';

const store = useStore();
const router = useRouter()
Expand All @@ -255,20 +282,42 @@ const userStore = useUserStore();
const cycleCount = computed(() => store.getters["count/getAssignedWorkEfforts"]);
const isScrollable = computed(() => store.getters["count/isCycleCountScrollable"])
const currentFacility = computed(() => userStore.getCurrentFacility)
const cycleCountStats = computed(() => (id) => store.getters["count/getCycleCountStats"](id))
// const cycleCountStats = computed(() => (id) => store.getters["count/getCycleCountStats"](id))
const userProfile = store.getters["user/getUserProfile"];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The userProfile constant is declared but never used within the component. It should be removed to avoid dead code.


const selectedSegment = ref("assigned");
const isScrollingEnabled = ref(false);
const contentRef = ref({});
const infiniteScrollRef = ref({});
let isLoading = ref(false);
const isAddSessionModalOpen = ref(false);

onIonViewDidEnter(async() => {
isLoading.value = true;
await fetchCycleCounts();
isLoading.value = false;
})

// TODO: Fetch the status description when the app loads.
function getSessionStatusDescription(statusId) {
if (!statusId) {
return "";
}
if (statusId === "INV_COUNT_CREATED") {
return "Created";
} else if (statusId === "CYCLE_CNT_IN_PRGS") {
return "In Progress";
} else if (statusId === "CYCLE_CNT_IN_CMPLTD") {
return "Completed";
} else {
return "Cancelled"
}
}
Comment on lines 302 to 316

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The getSessionStatusDescription function uses a chain of if/else if statements. This can be refactored into a more readable and maintainable structure using a map or an object lookup. This also makes it easier to add new statuses in the future. The hardcoded status IDs should also be defined as constants.

// TODO: Fetch the status description when the app loads.
const sessionStatusDescriptions: Record<string, string> = {
  "INV_COUNT_CREATED": "Created",
  "CYCLE_CNT_IN_PRGS": "In Progress",
  "CYCLE_CNT_IN_CMPLTD": "Completed",
};

function getSessionStatusDescription(statusId: string): string {
  if (!statusId) {
    return "";
  }
  return sessionStatusDescriptions[statusId] || "Cancelled";
}


function showAddNewSessionModal() {
isAddSessionModalOpen.value = true;
}

function enableScrolling() {
// Make sure refs exist and DOM is ready
const parentElement = contentRef.value?.$el
Expand Down Expand Up @@ -317,9 +366,10 @@ async function fetchCycleCounts(vSize, vIndex) {
pageSize,
pageIndex,
facilityId,
statusId: getStatusIdForCountsToBeFetched()
currentStatusId: getStatusIdForCountsToBeFetched()
};
await store.dispatch("count/fetchCycleCountsLists", payload);
console.log("This is store: ", store);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This console.log statement appears to be for debugging. It should be removed before merging.

await store.dispatch("count/getAssignedWorkEfforts", payload);
}

async function segmentChanged(value) {
Expand All @@ -335,33 +385,33 @@ function navigateToStoreView(count) {

function getStatusIdForCountsToBeFetched() {
if(selectedSegment.value === "assigned") {
return "INV_COUNT_ASSIGNED"
return "CYCLE_CNT_IN_PRGS"
} else if(selectedSegment.value === "pendingReview") {
return "INV_COUNT_REVIEW"
} else {
return "INV_COUNT_COMPLETED"
}
}

function getSubmissionDate(count) {
const history = cycleCountStats.value(count.inventoryCountImportId)?.statusHistory
if(!history) {
return "-";
}
// function getSubmissionDate(count) {
// const history = cycleCountStats.value(count.inventoryCountImportId)?.statusHistory
// if(!history) {
// return "-";
// }

const submissionStatus = history.toReversed().find((status) => status.statusId === "INV_COUNT_REVIEW")
return getDateWithOrdinalSuffix(submissionStatus?.statusDate)
}
// const submissionStatus = history.toReversed().find((status) => status.statusId === "INV_COUNT_REVIEW")
// return getDateWithOrdinalSuffix(submissionStatus?.statusDate)
// }

function getClosedDate(count) {
const history = cycleCountStats.value(count.inventoryCountImportId)?.statusHistory
if(!history) {
return "-";
}
// function getClosedDate(count) {
// const history = cycleCountStats.value(count.inventoryCountImportId)?.statusHistory
// if(!history) {
// return "-";
// }

const submissionStatus = history.toReversed().find((status) => status.statusId === "INV_COUNT_COMPLETED")
return getDateWithOrdinalSuffix(submissionStatus?.statusDate)
}
// const submissionStatus = history.toReversed().find((status) => status.statusId === "INV_COUNT_COMPLETED")
// return getDateWithOrdinalSuffix(submissionStatus?.statusDate)
// }
</script>

<style scoped>
Expand Down