broker_ demo
This commit is contained in:
parent
413c17e489
commit
628c50ad98
|
|
@ -21,6 +21,11 @@ const routes = [
|
|||
name: "documents",
|
||||
component: () => import("../views/document/Main.vue"),
|
||||
},
|
||||
{
|
||||
path: "broker-application",
|
||||
name: "broker-applications",
|
||||
component: () => import("../views/broker/Main.vue"),
|
||||
},
|
||||
{
|
||||
path: "ticket-list",
|
||||
name: "ticket-list",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { fetchWrapper } from "@/api";
|
||||
import { useAlertStore } from "@/stores";
|
||||
// import { useAuthStore } from "@/stores";
|
||||
|
||||
// const authStore = useAuthStore();
|
||||
// const { user } = storeToRefs(authStore);
|
||||
|
||||
const baseUrl = `${import.meta.env.VITE_API_URL}/api/broker-application`;
|
||||
|
||||
export const useBrokerStore = defineStore({
|
||||
id: "broker",
|
||||
state: () => ({
|
||||
brokerApplication: null,
|
||||
}),
|
||||
actions: {
|
||||
async getApplication() {
|
||||
try {
|
||||
const response = await fetchWrapper.get(`${baseUrl}`);
|
||||
|
||||
// update pinia state
|
||||
this.application = response["data"];
|
||||
} catch (error) {
|
||||
// const alertStore = useAlertStore();
|
||||
// alertStore.error(error);
|
||||
}
|
||||
},
|
||||
|
||||
async fileUpload(attachmentId, docFile) {
|
||||
|
||||
try {
|
||||
let formData = new FormData();
|
||||
formData.append("file", docFile);
|
||||
|
||||
const response = await fetchWrapper.post(
|
||||
`${baseUrl}/upload/${attachmentId}`,
|
||||
formData,
|
||||
true
|
||||
);
|
||||
|
||||
// update pinia state
|
||||
await this.getApplication();
|
||||
|
||||
const alertStore = useAlertStore();
|
||||
// trigger alert
|
||||
alertStore.success('Successfully uploaded');
|
||||
|
||||
} catch (error) {
|
||||
// const alertStore = useAlertStore();
|
||||
// alertStore.error(error);
|
||||
}
|
||||
},
|
||||
|
||||
async apply() {
|
||||
try {
|
||||
const response = await fetchWrapper.post(`${baseUrl}/apply`);
|
||||
|
||||
if(response != undefined) {
|
||||
|
||||
// update pinia state
|
||||
await this.getApplication();
|
||||
|
||||
const alertStore = useAlertStore();
|
||||
// trigger alert
|
||||
alertStore.success('Successfully applied');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// const alertStore = useAlertStore();
|
||||
// alertStore.error(error);
|
||||
}
|
||||
},
|
||||
|
||||
// async downloadQuestionnaire(token) {
|
||||
// try {
|
||||
// await fetchWrapper.get(`http://ba.digital-tps.tk/export-questionnaire/${token}`);
|
||||
// } catch (error) {
|
||||
// // const alertStore = useAlertStore();
|
||||
// // alertStore.error(error);
|
||||
// }
|
||||
// },
|
||||
},
|
||||
});
|
||||
|
|
@ -4,3 +4,4 @@ export * from "./countries";
|
|||
export * from "./categories";
|
||||
export * from "./application";
|
||||
export * from "./tickets";
|
||||
export * from "./broker";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,310 @@
|
|||
<template>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-4 gap-x-0 lg:gap-x-6 mt-5 pb-20">
|
||||
<div class="intro-y col-span-1">
|
||||
<div class="intro-y flex items-center my-8">
|
||||
<h2 class="text-lg font-medium mr-auto">{{ $t("DOCUMENTS") }}</h2>
|
||||
</div>
|
||||
<h4 class="text-md font-medium mr-auto pb-2">
|
||||
{{ $t("PLEASE_UPLOAD_DOCUMENTS") }}
|
||||
</h4>
|
||||
<div class="pb-6 text-primary">
|
||||
<!-- <i18n-t keypath="APPLICATION_PROCESS_DESCRIPTION" for="QUESTIONNAIRE_LINK">
|
||||
<a @click.prevent="downloadQuestionnaire" href="#" class="text-primary font-bold">{{ $t('QUESTIONNAIRE_LINK') }}</a>
|
||||
</i18n-t> -->
|
||||
{{ $t("APPLICATION_PROCESS_DESCRIPTION") }}
|
||||
</div>
|
||||
<!-- BEGIN: Files -->
|
||||
<div class="grid grid-cols-2 lg:grid-cols-1 xl:grid-cols-2 gap-3 mt-5">
|
||||
<div
|
||||
class="intro-y"
|
||||
>
|
||||
<div
|
||||
class="file box rounded-md px-5 pt-8 pb-5 px-3 sm:px-5 relative zoom-in"
|
||||
>
|
||||
|
||||
<a
|
||||
:href="baseUrl + QUESTIONNAIRE_LINK"
|
||||
class="w-3/5 file__icon file__icon--file mx-auto"
|
||||
@click.prevent="downloadQuestionnaire"
|
||||
>
|
||||
<div class="file__icon__file-name">
|
||||
{{ EXTENSION_QUESTIONNAIRE_FILE }}
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
:href="baseUrl + QUESTIONNAIRE_LINK"
|
||||
class="block font-medium mt-4 text-center truncate"
|
||||
@click.prevent="downloadQuestionnaire"
|
||||
>
|
||||
{{ $t("NAME_QUESTIONNAIRE_FILE") }}.{{ EXTENSION_QUESTIONNAIRE_FILE.toLowerCase() }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="intro-y"
|
||||
>
|
||||
<div
|
||||
class="file box rounded-md px-5 pt-8 pb-5 px-3 sm:px-5 relative zoom-in"
|
||||
>
|
||||
|
||||
<a :href="baseUrl + LETTER_LINK + accountType + '.' + EXTENSION_LETTER_FILE.toLowerCase()" class="w-3/5 file__icon file__icon--file mx-auto">
|
||||
<div class="file__icon__file-name">
|
||||
{{ EXTENSION_LETTER_FILE }}
|
||||
</div>
|
||||
</a>
|
||||
<a :href="baseUrl + LETTER_LINK + accountType + '.' + EXTENSION_LETTER_FILE.toLowerCase()" class="block font-medium mt-4 text-center truncate">
|
||||
{{ $t("NAME_LETTER_FILE") }}.{{ EXTENSION_LETTER_FILE.toLowerCase() }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END: Files -->
|
||||
</div>
|
||||
<div class="intro-y col-span-3">
|
||||
<!-- BEGIN: Upload -->
|
||||
<div class="intro-y flex items-center my-8">
|
||||
<h2 class="text-lg font-medium mr-auto">{{ $t("DOCUMENTS_TWO") }}</h2>
|
||||
</div>
|
||||
<h4 v-html="$t('PLEASE_UPLOAD_DOCUMENTS_TWO')" class="text-md font-medium mr-auto pb-2"></h4>
|
||||
<div class="pb-6 text-primary">
|
||||
{{ $t("APPLICATION_PROCESS_DESCRIPTION_TWO") }}
|
||||
</div>
|
||||
<div class="intro-y box p-5 mt-5" v-if="application">
|
||||
<div
|
||||
class="border border-slate-200/60 dark:border-darkmode-400 rounded-md p-5 divide-y md:divide-y-0"
|
||||
>
|
||||
<!-- <template v-if="application"> -->
|
||||
<!-- BEGIN 1.row -->
|
||||
<div
|
||||
class="flex flex-wrap md:flex-nowrap items-center pt-2"
|
||||
v-for="(attachment, index) in application.attachments"
|
||||
:key="attachment.id"
|
||||
>
|
||||
<div class="flex items-center self-end my-2 md:my-0 w-[55px]">
|
||||
<Tippy
|
||||
class="grow-0 mr-5"
|
||||
style="padding: 3px"
|
||||
:content="attachment.document_description"
|
||||
>
|
||||
<InfoIcon class="w-7 h-7 rounded-full text-white bg-primary" />
|
||||
</Tippy>
|
||||
|
||||
</div>
|
||||
<div class="grow text-md font-medium mr-5 self-center w-[calc(100%_-_75px)]">
|
||||
<a :class="{'text-primary': attachment.attachment_file_path}" :href="attachment.attachment_file_path ? baseUrl + attachment.attachment_file_path : 'javascript:;'">{{ index + 1 }}. {{ attachment.attachment_name }}</a>
|
||||
<p v-if="validationError[index]" class="text-danger">{{ validationError[index] }}</p>
|
||||
</div>
|
||||
<div class="flex items-center self-end my-2 md:my-0 w-full md:w-auto justify-end md:justify-start">
|
||||
|
||||
<label
|
||||
class="grow-0 btn mr-5"
|
||||
style="padding: 3px"
|
||||
:class="{'opacity-30': application.state === APPLICATION_APPROVED_STATE || application.state === APPLICATION_ACCEPTED_STATE, 'btn-outline-primary': !isLoadingDoc[index]}"
|
||||
|
||||
>
|
||||
<input type="file"
|
||||
@change="onFileChange(index, attachment, $event)"
|
||||
class="hidden"
|
||||
:disabled="application.state === APPLICATION_APPROVED_STATE || application.state === APPLICATION_ACCEPTED_STATE"
|
||||
/>
|
||||
<LoadingIcon class="w-7 h-7" icon="oval" color="#003197" v-if="isLoadingDoc[index]" />
|
||||
<UploadIcon class="w-7 h-7" v-else />
|
||||
</label>
|
||||
<div class="grow-0">
|
||||
<CheckCircleIcon class="w-7 h-7 text-success" :class="{'text-danger': !attachment.attachment_size}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END: 1.row -->
|
||||
<!-- </template> -->
|
||||
|
||||
|
||||
</div>
|
||||
<Alert
|
||||
class="font-medium alert-secondary mt-4"
|
||||
v-if="application.state === APPLICATION_REFINE_STATE"
|
||||
>
|
||||
{{ $t('APPLICATION_NEEDS_TO_BE_IMPROVED_TEXT_INTRO') }} <br>
|
||||
<template v-if="application.refine_note">
|
||||
{{ $t('REFINE_NOTE') }}: {{ application.refine_note }}
|
||||
</template>
|
||||
<br>
|
||||
<i18n-t
|
||||
v-if="application.ticket"
|
||||
keypath="APPLICATION_NEEDS_TO_BE_IMPROVED_TEXT"
|
||||
for="APPLICATION_NEEDS_TO_BE_IMPROVED_LINK"
|
||||
>
|
||||
<a
|
||||
@click.prevent="goToTicket(application.ticket.id)"
|
||||
href="#"
|
||||
class="font-bold underline"
|
||||
>
|
||||
{{ $t('APPLICATION_NEEDS_TO_BE_IMPROVED_LINK') }}
|
||||
</a>
|
||||
</i18n-t>
|
||||
</Alert>
|
||||
|
||||
<Alert
|
||||
class="font-medium mt-4 alert-primary"
|
||||
v-if="application.state === APPLICATION_ACCEPTED_STATE"
|
||||
>
|
||||
{{ $t('APPLICATION_ACCEPTED_BY', {accepted_by: application.accepted_by}) }}
|
||||
<br>
|
||||
{{ $t('APPLICATION_ACCEPTED_DATE', {accepted_date: normalizeDate(application.accepted_date)}) }}
|
||||
</Alert>
|
||||
<Alert
|
||||
class="font-medium text-white mt-4 alert-success"
|
||||
v-if="application.state === APPLICATION_APPROVED_STATE"
|
||||
>
|
||||
{{ $t('APPLICATION_APPROVED_BY', {approved_by: application.approved_by}) }}
|
||||
<br>
|
||||
{{ $t('APPLICATION_APPROVED_DATE', {approved_date: application.approved_date}) }}
|
||||
</Alert>
|
||||
<Alert
|
||||
class="font-medium alert-warning mt-4"
|
||||
v-if="application.state === APPLICATION_NEW_STATE"
|
||||
>
|
||||
{{ $t('APPLICATION_APPLIED') }}
|
||||
</Alert>
|
||||
<div
|
||||
class="flex justify-end mt-4"
|
||||
v-if="application.state === APPLICATION_NEW_STATE || application.state === APPLICATION_REFINE_STATE || application.state === APPLICATION_DRAFT_STATE"
|
||||
>
|
||||
<button class="btn btn-primary" @click="apply" :disabled="isApplying">
|
||||
{{ $t("APPLY") }}
|
||||
<LoadingIcon
|
||||
icon="oval"
|
||||
color="white"
|
||||
class="w-4 h-4 ml-2"
|
||||
v-if="isApplying"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END: Upload -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onBeforeMount, watch, ref } from "vue";
|
||||
import { useBrokerStore } from '@/stores';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import {
|
||||
BYTES_IN_KB,
|
||||
ALLOWED_FILE_TYPES,
|
||||
QUESTIONNAIRE_LINK,
|
||||
LETTER_LINK,
|
||||
APPLICATION_DRAFT_STATE,
|
||||
APPLICATION_NEW_STATE,
|
||||
APPLICATION_ACCEPTED_STATE,
|
||||
APPLICATION_REFINE_STATE,
|
||||
APPLICATION_APPROVED_STATE,
|
||||
EXTENSION_LETTER_FILE,
|
||||
EXTENSION_QUESTIONNAIRE_FILE,
|
||||
normalizeDate
|
||||
} from "@/helpers";
|
||||
import i18nn from "@/i18n";
|
||||
import { useAuthStore } from "@/stores";
|
||||
import router from "@/router";
|
||||
|
||||
const baseUrl = `${import.meta.env.VITE_API_URL}`;
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const { user } = storeToRefs(authStore);
|
||||
|
||||
const accountType = ref("");
|
||||
|
||||
const brokerStore = useBrokerStore();
|
||||
|
||||
const { application } = storeToRefs(brokerStore);
|
||||
|
||||
const docFile = ref(null);
|
||||
const validationError = ref({});
|
||||
|
||||
const isLoadingDoc = ref({});
|
||||
|
||||
const isApplying = ref(false);
|
||||
|
||||
const onFileChange = async (index, attachment, e) => {
|
||||
isLoadingDoc.value = {};
|
||||
isLoadingDoc.value[index] = true;
|
||||
|
||||
let files = e.target.files || e.dataTransfer.files
|
||||
|
||||
if (!files.length) {
|
||||
docFile.value = null;
|
||||
return
|
||||
}
|
||||
docFile.value = files[0]
|
||||
|
||||
if(fileIsValid(index, attachment)) {
|
||||
|
||||
await brokerStore.fileUpload(attachment.attachment_id, files[0]);
|
||||
}
|
||||
|
||||
isLoadingDoc.value[index] = false;
|
||||
}
|
||||
|
||||
const fileIsValid = (index, attachment) => {
|
||||
|
||||
validationError.value = {};
|
||||
|
||||
if(docFile.value.size > attachment.document_max_size * BYTES_IN_KB) {
|
||||
validationError.value[index] = i18nn.global.t('FILE_MAX_SIZE', {size: attachment.document_max_size});
|
||||
return false;
|
||||
}
|
||||
if(ALLOWED_FILE_TYPES.indexOf(docFile.value.type) < 0) {
|
||||
validationError.value[index] = i18nn.global.t('FILE_ALLOWED_TYPES', {file_types: ALLOWED_FILE_TYPES.join(', ')});
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const checkRequiredFilesUploaded = () => {
|
||||
|
||||
validationError.value = {};
|
||||
|
||||
let validToContinueRequest = true;
|
||||
|
||||
application.value.attachments.forEach((element, index) => {
|
||||
if(element.is_required && !element.attachment_file_path) {
|
||||
validationError.value[index] = i18nn.global.t('REQUIRED_VALIDATION');
|
||||
validToContinueRequest = false;
|
||||
}
|
||||
});
|
||||
|
||||
return validToContinueRequest;
|
||||
};
|
||||
|
||||
const apply = async () => {
|
||||
|
||||
if(checkRequiredFilesUploaded()) {
|
||||
isApplying.value = true;
|
||||
await brokerStore.apply();
|
||||
isApplying.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const goToTicket = (ticketId) => {
|
||||
router.push({ name: "ticket-list", query: { ticketId: ticketId } });
|
||||
};
|
||||
|
||||
const downloadQuestionnaire = async () => {
|
||||
// console.log(user.value.token);
|
||||
window.open(baseUrl + QUESTIONNAIRE_LINK + user.value.token, '_blank');
|
||||
// await brokerStore.downloadQuestionnaire(user.value.token);
|
||||
};
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await brokerStore.getApplication();
|
||||
if(!application.value) {
|
||||
router.push({ name: "error-page" });
|
||||
}
|
||||
console.log(application.value);
|
||||
|
||||
accountType.value = localStorage.getItem('account_type');
|
||||
});
|
||||
</script>
|
||||
Loading…
Reference in New Issue