Files
exchange-2.0/resources/assets/vue/components/general/sections/AdminWorkFlowSectionComponent.vue
T
2024-07-23 22:58:36 +08:00

498 lines
24 KiB
Vue

<template>
<div class="row justify-content-center align-items-center text-center" style="min-height: 80vh;">
<div class="col">
<h1 class="m-b-50" :class="{ 'text-success': timer }">{{ formattedTime }}</h1>
<div v-if="isLoading" class="row">
<div class="col">
<loading-component></loading-component>
</div>
</div>
<div v-else class="row">
<div class="col">
<div v-if="currentQuestion && Object.keys(currentQuestion).length && !apiFailed">
<h2 v-if="currentQuestion.text">{{ currentQuestion.text }}</h2>
<!-- <p>currentQuestionId - {{ currentQuestionId }}</p> -->
<!-- <p>currentOrder - {{ currentOrder }}</p> -->
<div v-if="currentQuestionId === '1688'">
<h1>Login Information</h1>
<div v-if="externalApiResponse.data"
class="row bg-master-light m-t-15 m-b-15 rounded padding-30 justify-content-center">
<div class="col-auto text-left">
<table>
<tr>
<td>
<h3><span class="bold d-inline-block m-r-15">ORDER Marking: </span></h3>
</td>
<td>
<h3><a :href="route('booking.details', externalApiResponse.data.booking_marking)"
target="_blank">{{ externalApiResponse.data.booking_marking
}}</a></h3>
</td>
</tr>
<tr>
<td>
<h3><span class="bold d-inline-block m-r-15">1688 LOGIN ID/EMAIL/PHONE:
</span></h3>
</td>
<td>
<h3>{{ externalApiResponse.data.account_no }}</h3>
</td>
</tr>
<tr>
<td>
<h3><span class="bold d-inline-block m-r-15">1688 LOGIN PASSWORD:
</span></h3>
</td>
<td>
<h3>{{ externalApiResponse.data.holder_name }}</h3>
</td>
</tr>
<tr>
<td>
<h3><span class="bold d-inline-block m-r-15">ALIPAY 6-DIGIT PAYMENT PIN:
</span></h3>
</td>
<td>
<h3>{{ externalApiResponse.data.pin }}</h3>
</td>
</tr>
</table>
</div>
</div>
<div v-else class="row bg-danger-light m-t-15 m-b-15 rounded padding-30">
<div class="col">
<h1 class="text-white">Api Failed. Please contact Tech Support</h1>
</div>
</div>
</div>
<div v-if="currentQuestionId === 'login_successful'">
<h3>
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
<a :href="route('booking.details', externalApiResponse.data.booking.marking)"
target="_blank">
{{ externalApiResponse.data.booking.marking }}
</a>
</h3>
<div v-if="externalApiResponse.data"
class="row bg-master-light m-t-15 m-b-15 rounded padding-30 justify-content-center">
<div class="col-auto text-left">
<h3>
<span class="bold d-inline-block m-r-15">ORDER Reference(s): </span>
</h3>
<div v-for="(attribute, index) in externalApiResponse.data.booking_attributes"
:key="index">
<h2>
{{ index + 1 + '. #' + attribute.value }}
</h2>
</div>
</div>
</div>
<div v-else class="row bg-danger-light m-t-15 m-b-15 rounded padding-30">
<div class="col">
<h1 class="text-white">Api Failed. Please contact Tech Support</h1>
</div>
</div>
</div>
<div v-if="currentQuestionId === 'approve_po'">
<div v-if="externalApiResponse.data" class="row">
<div class="col">
<h3>
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
<a :href="route('booking.details', externalApiResponse.data.marking)"
target="_blank">{{ externalApiResponse.data.marking }}</a>
</h3>
<div class="row b-a b-primary padding-30 bg-white m-t-25">
<div class="col">
<purchase-order-form-component :data="externalApiResponse.data"
:section="section"></purchase-order-form-component>
</div>
</div>
</div>
</div>
<div v-else class="row bg-danger-light m-t-15 m-b-15 rounded padding-30">
<div class="col">
<h1 class="text-white">Api Failed. Please contact Tech Support</h1>
</div>
</div>
</div>
<div v-if="currentQuestionId === 'fill_po'">
<div v-if="externalApiResponse.data" class="row">
<div class="col">
<h3>
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
<a :href="route('booking.details', externalApiResponse.data.marking)"
target="_blank">{{ externalApiResponse.data.marking }}</a>
</h3>
<div class="row b-a b-primary padding-30 bg-white m-t-25">
<div class="col">
<purchase-order-form-component :data="externalApiResponse.data"
section="section"></purchase-order-form-component>
</div>
</div>
</div>
</div>
<div v-else class="row bg-danger-light m-t-15 m-b-15 rounded padding-30">
<div class="col">
<h1 class="text-white">Api Failed. Please contact Tech Support</h1>
</div>
</div>
</div>
<div class="row" v-if="currentQuestion.answers">
<div class="col">
<div class="w-100 d-block text-center">
<button @click="goBack()"
v-if="questionIds.length && currentQuestion.show_back_btn != false"
class="btn btn-lg btn-default b-a b-grey d-inline-block rounded fs-20"
style="margin: 15px; padding: 15px 40px; min-width: 210px;">
Go Back
</button>
<button v-for="(answer, index) in currentQuestion.answers" :key="index"
@click="selectAnswer(answer.next)" :data-attr-next-step="answer.next"
class="btn btn-lg btn-primary d-inline-block rounded fs-20"
:class="getbuttonClass(answer.btn_color)"
style="margin: 15px; padding: 15px 40px; min-width: 210px;">
{{ answer.text }}
</button>
</div>
</div>
</div>
</div>
<div v-else>
<div class="row bg-danger-light m-t-15 m-b-15 rounded padding-30">
<div class="col">
<h1 class="text-white">
"{{ currentQuestionId }}" {{ apiFailed ? 'api Failed' : "is empty" }}
. Please contact Tech Support</h1>
</div>
</div>
<button class="btn btn-lg btn-primary d-inline-block rounded fs-20"
style="margin: 15px; padding: 15px 40px; min-width: 210px;"
@click="selectAnswer('refresh_page')">Refresh Page</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
section: 'initial_section',
questions: {
"node_0": {
"text": "Are you ready to work today?",
"answers": [
{ "text": "Yes", "next": "start_work" },
{ "text": "No", "next": "no_work" }
]
},
"start_work": {
"text": "What will you work on?",
"answers": [
{ "text": "1688", "next": "1688" },
{ "text": "Approve PO", "next": "approve_po" },
{ "text": "Fill PO", "next": "fill_po" }
]
},
"no_work": {
"text": "Come back when you are ready to work",
"answers": [
{ "text": "Refresh Page", "next": "refresh_page" }
]
},
"1688": {
"load_api": this.route("api.admin_work_flow.fetch_oldest_order"),
"answers": [
{ "text": "Login Issue", "next": "login_issue", 'btn_color': "warning" },
{ "text": "Login Successful", "next": "login_successful" }
],
},
"login_issue": {
"text": "Login Issues",
"answers": [
{ "text": "Need TAC", "next": "Need TAC" },
{ "text": "Wrong login details", "next": "Wrong login details" },
{ "text": "Others", "next": "Others" },
{ "text": "Order Cancelled", "next": "Order Cancelled" }
]
},
"login_successful": {
"load_api": 'fetch_model_attributes',
"answers": [
{ "text": "Can't verify order?", "next": "issue_order" },
{ "text": "Order Verified", "next": "order_verify" },
]
},
"issue_order": {
"answers": [
{ "text": "Amount not found", "next": "issue_submit" },
{ "text": "Plus Member", "next": "issue_submit" },
{ "text": "Others", "next": "po_others" },
{ "text": "Customer Did not verify 1688 account", "next": "issue_submit" },
{ "text": "WorldFirst account linked another account", "next": "issue_submit" },
]
},
"order_verify": {
// fetch order amount
"load_api": this.route("api.admin_work_flow.fetch_model_attributes"),
"answers": [
{ "text": "Yes", "next": "order_verification" }
]
},
"order_verification": {
"text": "Are You Sure this is the correct amount?",
"answers": [
{ "text": "Yes", "next": "proceed_order" },
{ "text": "No", "next": "insufficient_order" }
]
},
"proceed_order": {
"text": "Please proceed the order on 1688",
"answers": [
{ "text": "Got Issue", "next": "1688_order_issue" },
{ "text": "Done", "next": "1688_submit" }
]
},
"1688_order_issue": {
"answers": [
{ "text": "Wrong Pin Number", "next": "1688_issue_submit" },
{ "text": "Not Enough Stock", "next": "1688_issue_submit" },
{ "text": "Others", "next": "1688_others" },
{ "text": "No CrossBoarder", "next": "1688_issue_submit" },
{ "text": "AngPau", "next": "1688_issue_submit" }
]
},
"1688_others": {},
"1688_submit": {},
"1688_issue_submit": {
"text": "Issue has been submitted",
"answers": [
{ "text": "Stop Working", "next": "stop_working" },
{ "text": "Next Order", "next": "1688" }
]
},
"refund_request": {
"text": "Refund Request sent",
"answers": [
{ "text": "Stop Working", "next": "stop_working" },
{ "text": "Next PO", "next": "approve_po" }
]
},
"approve_po": {
// "text": "Order Number",
"load_api": this.route("api.admin_work_flow.fetch_pending_approve_po"),
"answers": [
{ "text": "Approve", "next": "po_approved" },
{ "text": "Edit PO", "next": "edit_po" },
{ "text": "Reject", "next": "reject_po" }
]
},
"fill_po": {
// "text": "Order Number",
"load_api": this.route("api.admin_work_flow.fetch_pending_fill_po"),
"answers": [
{ "text": "Approve", "next": "po_approved" },
{ "text": "Edit PO", "next": "edit_po" },
{ "text": "Reject", "next": "reject_po" }
]
},
"po_filled": {
"text": "PO Filled",
"answers": [
{ "text": "Stop Working", "next": "stop_working" },
{ "text": "Next PO", "next": "approve_po" }
]
},
"submit_po": {},
"po_approved": {
"text": "PO Approved",
"answers": [
{ "text": "Stop Working", "next": "stop_working" },
{ "text": "Next PO", "next": "approve_po" }
]
},
"edit_po": {},
"po_others": {},
"reject_po": {
"text": "PO Rejected",
"answers": [
{ "text": "Issue", "next": "issue_po" },
]
},
"issue_po": {
"answers": [
{ "text": "Sensitive Goods", "next": "issue_submit" },
{ "text": "Others", "next": "po_others" },
{ "text": "Stop Working", "next": "stop_working" },
{ "text": "Next PO", "next": "approve_po" }
]
},
"issue_submit": {
"text": "Issue has been submitted",
"answers": [
{ "text": "Stop Working", "next": "stop_working" },
{ "text": "Next Order", "next": "approve_po" }
]
},
"1688_issue_submitted": {
"text": "Issue has been submitted",
"answers": [
{ "text": "Stop Working", "next": "stop_working" },
{ "text": "Next Order", "next": "1688" }
]
},
"stop_working": {
"text": "Thank you for your work!",
"answers": [
{ "text": "Refresh Page", "next": "refresh_page" }
]
}
},
currentQuestionId: null,
questionIds: [],
isLoading: false,
externalApiResponse: { data: null },
timer: null,
elapsedTime: 0,
stepTime: 0,
interval: 1000,
sessionId: null,
currentOrder: null,
apiFailed: false,
};
},
computed: {
currentQuestion() { return this.questions[this.currentQuestionId] || {}; },
formattedTime() {
const hours = String(Math.floor(this.elapsedTime / 3600)).padStart(2, '0');
const minutes = String(Math.floor((this.elapsedTime % 3600) / 60)).padStart(2, '0');
const seconds = String(this.elapsedTime % 60).padStart(2, '0');
return `${hours}:${minutes}:${seconds}`;
}
},
mounted() {
this.currentQuestionId = 'node_0';
if (this.sessionId == null) {
this.sessionId = this.generateSessionId();
}
},
methods: {
startTimer() {
if (!this.timer) {
this.timer = setInterval(() => {
this.elapsedTime++;
this.stepTime++;
}, this.interval);
} else {
console.log("time has already started");
}
},
stopTimer() { clearInterval(this.timer); this.timer = null; },
startWork() { this.startTimer(); },
endWork() { this.stopTimer(); console.log("work is ended"); },
selectAnswer(nextQuestionId, goBack = false) {
if (nextQuestionId === 'refresh_page') return window.location.reload();
if (this.currentQuestionId === 'login_issue') {
this.callApi(this.route('api.admin_work_flow.create_1688_login_issue_remark'), 'post', 'section', {
'booking_id': this.currentOrder.booking_id,
'remark': nextQuestionId,
'user_id': this.$store.getters.getUserId,
}, false, false);
nextQuestionId = 'issue_submit'
}
if (!Object.keys(this.questions[nextQuestionId]).length) this.endWork();
if (nextQuestionId === 'start_work') this.startWork();
if (nextQuestionId === 'stop_working') this.stopTimer();
// exclude some node to click back
if (!['node_0'].includes(this.currentQuestionId) && !goBack) {
this.questionIds.push(this.currentQuestionId);
this.callLogApi(this.currentQuestionId, nextQuestionId);
}
if (!['node_0'].includes(this.currentQuestionId) && goBack) {
this.callLogApi(this.currentQuestionId, 'goBack');
}
if (this.questions[nextQuestionId]?.load_api) this.callApi(this.questions[nextQuestionId].load_api, 'get', 'section');
this.currentQuestionId = nextQuestionId;
},
callLogApi(currentNode, nextNode) {
// call api to recorrd timestamp
var logBody = {
'current_node': currentNode,
'next_node': nextNode,
'seconds': this.stepTime,
'user_id': this.$store.getters.getUserId,
'session_id': this.sessionId,
};
this.callApi(this.route('api.admin_work_flow.add_workflow_timestamp'), 'post', 'section', logBody, false, false);
this.stepTime = 0;
},
goBack() {
var prevId = this.questionIds.pop();
this.selectAnswer(prevId, true);
},
generateSessionId() {
return 'xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
},
callApi(url, method, section, parameters = null, showLoadingAnimation = true, storeResponseBody = true) {
if (url == 'fetch_model_attributes') {
url = this.route("api.admin_work_flow.fetch_model_attributes", this.currentOrder.booking_id)
}
if (showLoadingAnimation) this.isLoading = true;
this.$store.dispatch('crudRequest', { endpoint: url, method, parameters: parameters })
.then(response => response.json().then(data => ({ data, ok: response.ok, status: response.status })))
.then(apiResponse => {
if (storeResponseBody) {
this.externalApiResponse = apiResponse.data;
}
if (apiResponse.ok) {
this.successHandler(apiResponse.data);
} else {
// Improved error handling
this.handleError(apiResponse.data, apiResponse.status);
}
if (showLoadingAnimation) {
this.isLoading = false;
}
if (url === this.route("api.admin_work_flow.fetch_oldest_order")) {
this.currentOrder = apiResponse.data.data;
}
})
.catch(error => {
// Handle network or other unexpected errors
console.error('Unexpected error:', error);
this.errorHandler({ message: 'An unexpected error occurred. Please try again later.' }, 500);
this.apiFailed = true;
if (showLoadingAnimation) {
this.isLoading = false;
}
});
},
getbuttonClass(className) {
return 'btn-' + className;
},
}
};
</script>