Merge branch 'develop'
This commit is contained in:
commit
55c15585f8
22 changed files with 403 additions and 115 deletions
|
|
@ -57,7 +57,9 @@ api.interceptors.response.use(
|
|||
persistent: true,
|
||||
enablei18n: true,
|
||||
message: `dialog.backend.${parseError(err.response.status, err.response.data)}`,
|
||||
action: () => {},
|
||||
action: () => {
|
||||
if (err.response.status === 401) window.location.reload();
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ const firstName = defineModel<string>('firstName');
|
|||
const namePrefix = defineModel<string>('namePrefix');
|
||||
const passportNumber = defineModel<string>('passportNumber');
|
||||
|
||||
const passportValidator = /[a-zA-Z]{1}[a-zA-Z0-9]{1}[0-9]{5,7}$/;
|
||||
|
||||
const genderOptions = ref<Record<string, unknown>[]>([]);
|
||||
let genderFilter: (
|
||||
value: string,
|
||||
|
|
@ -275,7 +277,6 @@ watch(
|
|||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
|
||||
<q-input
|
||||
:for="`${prefixId}-input-previous-passport-Number`"
|
||||
:dense="dense"
|
||||
|
|
@ -287,8 +288,9 @@ watch(
|
|||
v-model="previousPassportRef"
|
||||
:rules="[
|
||||
(v) =>
|
||||
(!!v && v.length === 6) ||
|
||||
$t('form.error.requireLength', { msg: 6 }),
|
||||
!v ||
|
||||
passportValidator.test(v) ||
|
||||
$t('form.error.passportFormat'),
|
||||
]"
|
||||
/>
|
||||
|
||||
|
|
@ -484,7 +486,9 @@ watch(
|
|||
:label="$t('customerEmployee.form.passportNo')"
|
||||
v-model="passportNumber"
|
||||
:rules="[
|
||||
(val) => (val && val.length > 0) || $t('form.error.required'),
|
||||
(val) => !!val || $t('form.error.required'),
|
||||
(val) =>
|
||||
passportValidator.test(val) || $t('form.error.passportFormat'),
|
||||
]"
|
||||
/>
|
||||
|
||||
|
|
|
|||
127
src/components/shared/select/SelectAgent.vue
Normal file
127
src/components/shared/select/SelectAgent.vue
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { getRole } from 'src/services/keycloak';
|
||||
|
||||
import { createSelect, SelectProps } from './select';
|
||||
import SelectInput from '../SelectInput.vue';
|
||||
|
||||
import { User } from 'src/stores/user/types';
|
||||
|
||||
import useStore from 'src/stores/user';
|
||||
|
||||
type SelectOption = User;
|
||||
|
||||
const value = defineModel<string | null | undefined>('value', {
|
||||
required: true,
|
||||
});
|
||||
const valueOption = defineModel<SelectOption>('valueOption', {
|
||||
required: false,
|
||||
});
|
||||
|
||||
const selectOptions = ref<SelectOption[]>([]);
|
||||
|
||||
const { fetchList: getList, fetchById: getById } = useStore();
|
||||
|
||||
defineEmits<{
|
||||
(e: 'create'): void;
|
||||
}>();
|
||||
|
||||
type ExclusiveProps = {
|
||||
codeOnly?: boolean;
|
||||
selectFirstValue?: boolean;
|
||||
branchVirtual?: boolean;
|
||||
checkRole?: string[];
|
||||
};
|
||||
|
||||
const props = defineProps<SelectProps<typeof getList> & ExclusiveProps>();
|
||||
|
||||
const { getOptions, setFirstValue, getSelectedOption, filter } =
|
||||
createSelect<SelectOption>(
|
||||
{
|
||||
value,
|
||||
valueOption,
|
||||
selectOptions,
|
||||
getList: async (query) => {
|
||||
const ret = await getList({
|
||||
query,
|
||||
...props.params,
|
||||
includeBranch: true,
|
||||
pageSize: 99999,
|
||||
userType: 'DELEGATE',
|
||||
status: 'ACTIVE',
|
||||
});
|
||||
if (ret) return ret.result;
|
||||
},
|
||||
getByValue: async (id) => {
|
||||
const ret = await getById(id);
|
||||
if (ret) return ret;
|
||||
},
|
||||
},
|
||||
{ valueField: 'id' },
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
await getOptions();
|
||||
|
||||
if (props.autoSelectOnSingle && selectOptions.value.length === 1) {
|
||||
setFirstValue();
|
||||
}
|
||||
|
||||
if (props.selectFirstValue) {
|
||||
setDefaultValue();
|
||||
} else await getSelectedOption();
|
||||
});
|
||||
|
||||
function setDefaultValue() {
|
||||
setFirstValue();
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<SelectInput
|
||||
v-model="value"
|
||||
incremental
|
||||
option-value="id"
|
||||
:label
|
||||
:placeholder
|
||||
:readonly
|
||||
:disable="disabled"
|
||||
:option="selectOptions"
|
||||
:hide-selected="false"
|
||||
:fill-input="false"
|
||||
:rules="[
|
||||
(v: string) => !props.required || !!v || $t('form.error.required'),
|
||||
]"
|
||||
@filter="filter"
|
||||
>
|
||||
<template #selected-item="{ opt }">
|
||||
{{
|
||||
$i18n.locale === 'tha'
|
||||
? opt.firstName + ' ' + opt.lastName
|
||||
: opt.firstNameEN + ' ' + opt.lastNameEN
|
||||
}}
|
||||
</template>
|
||||
|
||||
<template #option="{ opt, scope }">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<span class="row items-center">
|
||||
{{
|
||||
$i18n.locale === 'tha'
|
||||
? opt.firstName + ' ' + opt.lastName
|
||||
: opt.firstNameEN + ' ' + opt.lastNameEN
|
||||
}}
|
||||
</span>
|
||||
</q-item>
|
||||
|
||||
<q-separator class="q-mx-sm" />
|
||||
</template>
|
||||
|
||||
<template #append v-if="clearable">
|
||||
<q-icon
|
||||
v-if="!readonly && value"
|
||||
name="mdi-close-circle"
|
||||
@click.stop="value = ''"
|
||||
class="cursor-pointer clear-btn"
|
||||
/>
|
||||
</template>
|
||||
</SelectInput>
|
||||
</template>
|
||||
|
|
@ -139,6 +139,7 @@ export default {
|
|||
import: 'Import',
|
||||
numberOfDay: 'Number of days',
|
||||
other: 'Other',
|
||||
agencyAddress: 'Agency Address',
|
||||
},
|
||||
|
||||
menu: {
|
||||
|
|
@ -299,6 +300,7 @@ export default {
|
|||
branchNameField: "Only letters, numbers, or the characters . , - ' &.",
|
||||
branchNameENField:
|
||||
"Only English letters, numbers, or the characters . , - ' &.",
|
||||
passportFormat: 'Please enter the passport number in the correct format.',
|
||||
},
|
||||
warning: {
|
||||
title: 'Warning {msg}',
|
||||
|
|
@ -958,6 +960,10 @@ export default {
|
|||
Validate: 'Validate',
|
||||
Complete: 'Complete',
|
||||
Canceled: 'Canceled',
|
||||
Restart: 'go proceed again',
|
||||
receive: {
|
||||
Canceled: 'Canceled',
|
||||
},
|
||||
},
|
||||
|
||||
receiveTask: 'Receive Task',
|
||||
|
|
@ -977,7 +983,7 @@ export default {
|
|||
noRequestAvailable: 'There is no request list available for processing',
|
||||
validate: 'Validate',
|
||||
done: 'Done',
|
||||
confirmValidate: 'Confirm Validate',
|
||||
confirmEndWork: 'Confirm end of work',
|
||||
},
|
||||
|
||||
dialog: {
|
||||
|
|
@ -1012,6 +1018,10 @@ export default {
|
|||
confirmValidate: 'Do you confirm the validation?',
|
||||
warningSelectDeliveryStaff:
|
||||
'You have not yet selected a document delivery staff.',
|
||||
|
||||
confirmEndWorkWarning:
|
||||
"Do you want to end the work now? The current statuses 'Pending', 'In Progress', 'To Be Reprocessed' will be changed to 'Redo All'.",
|
||||
confirmEndWork: 'Do you want to end the work?',
|
||||
},
|
||||
action: {
|
||||
ok: 'OK',
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ export default {
|
|||
next: 'ถัดไป',
|
||||
numberOfDay: 'จำนวนวัน',
|
||||
other: 'อื่นๆ',
|
||||
agencyAddress: 'ที่อยู่หน่วยงาน ',
|
||||
},
|
||||
|
||||
menu: {
|
||||
|
|
@ -298,6 +299,7 @@ export default {
|
|||
branchNameField: "โปรดใช้ตัวอักษร ตัวเลข หรือ . , - ' & เท่านั้น",
|
||||
branchNameENField:
|
||||
"โปรดใช้ตัวอักษรภาษาอังกฤษ ตัวเลข หรือ . , - ' & เท่านั้น",
|
||||
passportFormat: 'กรุณากรอกหมายเลขพาสปอร์ตให้ถูกต้องตามรูปแบบ',
|
||||
},
|
||||
warning: {
|
||||
title: 'แจ้งเตือน {msg}',
|
||||
|
|
@ -941,12 +943,15 @@ export default {
|
|||
InProgress: 'กำลังดำเนินการ ',
|
||||
Success: 'ดำเนินการสำเร็จ',
|
||||
Failed: 'ดำเนินการไม่สำเร็จ',
|
||||
Redo: 'ทำใหม่',
|
||||
Redo: 'ยกเลิกรอดำเนินการใหม่',
|
||||
Validate: 'ตรวจสอบความถูกต้อง',
|
||||
Complete: 'ดำเนินการเสร็จสิ้น',
|
||||
Canceled: ' รายการคำขอถูกยกเลิก',
|
||||
Canceled: 'รายการคำขอถูกยกเลิก',
|
||||
Restart: 'ไปดำเนินการใหม่',
|
||||
receive: {
|
||||
Canceled: 'ยกเลิก',
|
||||
},
|
||||
},
|
||||
|
||||
receiveTask: 'รับงาน',
|
||||
receiveScan: 'รับงานแบบสแกน',
|
||||
receiveCustom: 'รับงานแบบเลือกเอง',
|
||||
|
|
@ -964,7 +969,7 @@ export default {
|
|||
noRequestAvailable: 'ไม่มีใบรายการคำขอที่สามารถดำเนินการได้',
|
||||
validate: 'ตรวจสอบสินค้า',
|
||||
done: 'ดำเนินการแล้ว',
|
||||
confirmValidate: 'ยืนยันการตรวจสอบ',
|
||||
confirmEndWork: 'ยืนยันการจบงาน',
|
||||
},
|
||||
|
||||
dialog: {
|
||||
|
|
@ -996,7 +1001,8 @@ export default {
|
|||
confirmSavingStatus:
|
||||
'คุณต้องการยืนยันการบันทึกข้อมูลการเปลี่ยนสถานะใช่หรือไม่',
|
||||
confirmSending: 'ยืนยันการส่งงานใช่หรือไม่',
|
||||
confirmValidate: 'ยืนยันการตรวจสอบใช่หรือไม่',
|
||||
confirmEndWorkWarning: `ท่านต้องการให้ดำเนินการจบงานในขณะนี้หรือไม่? สถานะปัจจุบันที่แสดงว่า 'รอดำเนินการ' 'กำลังดำเนินการ' 'ไปดำเนินการใหม่' จะถูกเปลี่ยนเป็น 'ทำใหม่ทั้งหมด'`,
|
||||
confirmEndWork: 'ท่านต้องการจบงานใช่หรือไม่',
|
||||
},
|
||||
action: {
|
||||
ok: 'ยืนยัน',
|
||||
|
|
|
|||
|
|
@ -4630,7 +4630,7 @@ const emptyCreateDialog = ref(false);
|
|||
"
|
||||
class="q-mb-xl"
|
||||
/>
|
||||
<FormPerso
|
||||
<FormPerson
|
||||
id="drawer-form-personal"
|
||||
prefix-id="drawer-info-employee"
|
||||
dense
|
||||
|
|
|
|||
|
|
@ -1,13 +1,5 @@
|
|||
<script lang="ts" setup>
|
||||
import useUserStore from 'stores/user';
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import SelectInput from 'src/components/shared/SelectInput.vue';
|
||||
import { QSelect } from 'quasar';
|
||||
|
||||
const userStore = useUserStore();
|
||||
const { locale } = useI18n({ useScope: 'global' });
|
||||
import SelectAgent from 'src/components/shared/select/SelectAgent.vue';
|
||||
|
||||
defineProps<{
|
||||
readonly?: boolean;
|
||||
|
|
@ -20,41 +12,6 @@ const officeTel = defineModel<string>('officeTel');
|
|||
const agent = defineModel<string>('agent');
|
||||
|
||||
const agentUserId = defineModel<string>('agentUserId');
|
||||
|
||||
const agentOptions = ref<{ id: string; label: string; labelEN: string }[]>([]);
|
||||
|
||||
async function fetchAgentOptions(query: string) {
|
||||
const res = await userStore.fetchList({
|
||||
includeBranch: true,
|
||||
query: query,
|
||||
pageSize: 99999,
|
||||
userType: 'DELEGATE',
|
||||
status: 'ACTIVE',
|
||||
});
|
||||
|
||||
if (res) {
|
||||
agentOptions.value = res.result.map((v) => ({
|
||||
id: v.id,
|
||||
label: v.firstName + ' ' + v.lastName,
|
||||
labelEN: v.firstNameEN + ' ' + v.lastNameEN,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
async function filter(val: string, update: (...args: unknown[]) => void) {
|
||||
update(
|
||||
async () => {
|
||||
await fetchAgentOptions(val);
|
||||
},
|
||||
|
||||
(ref: QSelect) => {
|
||||
if (val !== '' && ref.options && ref.options?.length > 0) {
|
||||
ref.setOptionIndex(-1);
|
||||
ref.moveOptionSelection(1, true);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -153,18 +110,12 @@ async function filter(val: string, update: (...args: unknown[]) => void) {
|
|||
</template>
|
||||
</q-input>
|
||||
|
||||
<SelectInput
|
||||
@click="fetchAgentOptions"
|
||||
<SelectAgent
|
||||
:label="$t('customer.form.agent')"
|
||||
v-model:value="agentUserId"
|
||||
:readonly
|
||||
incremental
|
||||
v-model="agentUserId"
|
||||
id="quotation-branch"
|
||||
class="col-md-6 col-12"
|
||||
:option="agentOptions"
|
||||
:label="$t('customer.form.agent')"
|
||||
option-value="id"
|
||||
:option-label="locale === 'eng' ? 'labelEN' : 'label'"
|
||||
@filter="(val: string, update) => filter(val, update)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -59,10 +59,12 @@ const { state: customerFormState, currentFormData: customerFormData } =
|
|||
const { currentMyBranch } = storeToRefs(userBranch);
|
||||
|
||||
const fieldSelectedOption = computed(() => {
|
||||
return columnQuotation.map((v) => ({
|
||||
label: v.label,
|
||||
value: v.name,
|
||||
}));
|
||||
return columnQuotation
|
||||
.filter((v) => v.name !== 'action')
|
||||
.map((v) => ({
|
||||
label: v.label,
|
||||
value: v.name,
|
||||
}));
|
||||
});
|
||||
const special = ref(false);
|
||||
const branchId = ref('');
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { getInstance } from 'src/services/keycloak';
|
|||
import { useNavigator } from 'src/stores/navigator';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { initLang } from 'src/utils/ui';
|
||||
|
||||
const $q = useQuasar();
|
||||
const { locale } = useI18n();
|
||||
|
|
@ -25,6 +26,8 @@ function sendMessage() {
|
|||
}
|
||||
|
||||
onMounted(() => {
|
||||
initLang();
|
||||
currentLocale.value = locale.value;
|
||||
navigatorStore.current.title = 'menu.dms';
|
||||
navigatorStore.current.path = [
|
||||
{
|
||||
|
|
@ -47,12 +50,14 @@ watch(
|
|||
watch([locale, $q.dark], () => {
|
||||
sendMessage();
|
||||
});
|
||||
|
||||
const currentLocale = ref(locale.value);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<iframe
|
||||
ref="iframe"
|
||||
:src="`${EDM_SERVICE}/?at=${at}&rt=${rt}&lang=${locale}`"
|
||||
:src="`${EDM_SERVICE}/?at=${at}&rt=${rt}&lang=${currentLocale}`"
|
||||
frameborder="0"
|
||||
class="full-width full-height rounded"
|
||||
allowtransparency="true"
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ async function fetchStats() {
|
|||
function triggerCancel(id: string) {
|
||||
dialogWarningClose(t, {
|
||||
message: t('form.warning.cancel'),
|
||||
actionText: t('dialog.action.ok'),
|
||||
action: async () => {
|
||||
const res = await requestListStore.cancelRequest(id);
|
||||
if (res) {
|
||||
|
|
@ -222,6 +223,10 @@ watch([() => pageState.inputSearch, () => pageState.statusFilter], () =>
|
|||
label: $t('requestList.status.Completed'),
|
||||
value: RequestDataStatus.Completed,
|
||||
},
|
||||
{
|
||||
label: $t('requestList.status.Canceled'),
|
||||
value: RequestDataStatus.Canceled,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
<q-select
|
||||
|
|
|
|||
|
|
@ -11,11 +11,14 @@ import {
|
|||
PropString,
|
||||
} from 'src/stores/product-service/types';
|
||||
|
||||
import { Attributes } from 'src/stores/request-list/types';
|
||||
import { Attributes, RequestData } from 'src/stores/request-list/types';
|
||||
|
||||
import { getCustomerName, getEmployeeName } from 'src/stores/utils';
|
||||
import useOptionStore from 'src/stores/options';
|
||||
import { useRequestList } from 'src/stores/request-list';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { locale } = useI18n();
|
||||
const optionStore = useOptionStore();
|
||||
const requestListStore = useRequestList();
|
||||
|
||||
|
|
@ -24,6 +27,7 @@ const props = withDefaults(
|
|||
id: string;
|
||||
readonly?: boolean;
|
||||
propertiesToShow: (PropString | PropNumber | PropDate | PropOptions)[];
|
||||
requestListData: RequestData;
|
||||
}>(),
|
||||
{
|
||||
id: '',
|
||||
|
|
@ -31,6 +35,8 @@ const props = withDefaults(
|
|||
},
|
||||
);
|
||||
|
||||
const readonlyField = ['quotationNo', 'contactPerson', 'telephone', 'employer'];
|
||||
|
||||
const formRemark = ref<string>('');
|
||||
const formData = ref<{
|
||||
[field: string]: string | number | null | undefined;
|
||||
|
|
@ -77,10 +83,22 @@ function triggerEdit() {
|
|||
}
|
||||
|
||||
function assignToForm() {
|
||||
const requestData = props.requestListData;
|
||||
// console.log(requestData);
|
||||
formRemark.value = attributes.value?.remark || '';
|
||||
formData.value = JSON.parse(
|
||||
JSON.stringify(attributes.value?.properties || {}),
|
||||
);
|
||||
formData.value['quotationNo'] = requestData.quotation.code;
|
||||
formData.value['contactPerson'] = requestData.quotation.contactName;
|
||||
formData.value['telephone'] = requestData.quotation.contactTel;
|
||||
formData.value['employee'] = getEmployeeName(requestData.employee, {
|
||||
locale: locale.value,
|
||||
});
|
||||
formData.value['employer'] = getCustomerName(
|
||||
requestData.quotation.customerBranch,
|
||||
{ locale: locale.value },
|
||||
);
|
||||
}
|
||||
|
||||
defineEmits<{
|
||||
|
|
@ -143,7 +161,7 @@ defineEmits<{
|
|||
{{ i + 1 }} {{ optionStore.mapOption(prop.fieldName) }}
|
||||
</article>
|
||||
<PropertiesToInput
|
||||
:readonly="!state.isEdit"
|
||||
:readonly="!state.isEdit || readonlyField.includes(prop.fieldName)"
|
||||
:prop="prop"
|
||||
:placeholder="optionStore.mapOption(prop.fieldName)"
|
||||
v-model="formData[prop.fieldName]"
|
||||
|
|
|
|||
|
|
@ -816,6 +816,7 @@ function goToQuotation(
|
|||
"
|
||||
/>
|
||||
<PropertiesExpansion
|
||||
:request-list-data="data"
|
||||
:id="value.id"
|
||||
:readonly="
|
||||
data.requestDataStatus === RequestDataStatus.Canceled
|
||||
|
|
|
|||
|
|
@ -69,6 +69,10 @@ function taskOrderStatus(value: TaskOrderStatus, type: 'status' | 'color') {
|
|||
status: 'taskOrder.sentTask',
|
||||
color: '--blue-6-hsl',
|
||||
},
|
||||
[TaskOrderStatus.Restart]: {
|
||||
status: 'taskOrder.status.Restart',
|
||||
color: '--red-5-hsl',
|
||||
},
|
||||
}[value][type];
|
||||
}
|
||||
|
||||
|
|
@ -157,7 +161,13 @@ const emit = defineEmits<{
|
|||
{{ props.row.code || '-' }}
|
||||
</div>
|
||||
</q-td>
|
||||
<q-td v-if="visibleColumns.includes('issueBranch')">-</q-td>
|
||||
<q-td v-if="visibleColumns.includes('issueBranch')">
|
||||
{{
|
||||
$i18n.locale === 'eng'
|
||||
? props.row.registeredBranch.nameEN || '-'
|
||||
: props.row.registeredBranch.name || '-'
|
||||
}}
|
||||
</q-td>
|
||||
<q-td v-if="visibleColumns.includes('institution')">
|
||||
{{
|
||||
$i18n.locale === 'eng'
|
||||
|
|
@ -251,7 +261,10 @@ const emit = defineEmits<{
|
|||
:custom-data="[
|
||||
{
|
||||
label: $t('taskOrder.issueBranch'),
|
||||
value: props.row.issueBranch,
|
||||
value:
|
||||
$i18n.locale === 'eng'
|
||||
? props.row.registeredBranch.nameEN || '-'
|
||||
: props.row.registeredBranch.name || '-',
|
||||
},
|
||||
{
|
||||
label: $t('general.agencies'),
|
||||
|
|
|
|||
|
|
@ -75,7 +75,9 @@ function hideIcon() {
|
|||
:label="
|
||||
currStatus?.value === TaskStatus.Validate && type === 'order'
|
||||
? $t('taskOrder.done')
|
||||
: $t(`taskOrder.status.${status}`)
|
||||
: currStatus?.value === TaskStatus.Redo && type === 'receive'
|
||||
? $t(`taskOrder.status.receive.Canceled`)
|
||||
: $t(`taskOrder.status.${status}`)
|
||||
"
|
||||
class="text-capitalize text-weight-regular product-status rounded"
|
||||
:class="{
|
||||
|
|
@ -90,7 +92,8 @@ function hideIcon() {
|
|||
negative:
|
||||
currStatus?.value === TaskStatus.Failed ||
|
||||
currStatus?.value === TaskStatus.Redo ||
|
||||
currStatus?.value === TaskStatus.Canceled,
|
||||
currStatus?.value === TaskStatus.Canceled ||
|
||||
currStatus?.value === TaskStatus.Restart,
|
||||
'pointer-events-none': {
|
||||
order: !['Success', 'Failed', 'Validate'].includes(status || ''),
|
||||
receive: status !== TaskStatus.InProgress,
|
||||
|
|
@ -110,7 +113,9 @@ function hideIcon() {
|
|||
(v) => v.value === TaskStatus.Complete,
|
||||
),
|
||||
Failed: taskStatusOrderToggle.filter(
|
||||
(v) => v.value === TaskStatus.Redo,
|
||||
(v) =>
|
||||
v.value === TaskStatus.Redo ||
|
||||
v.value === TaskStatus.Restart,
|
||||
),
|
||||
Validate: taskStatusOrderToggle.filter(
|
||||
(v) => v.value !== TaskStatus.InProgress,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ export const taskStatusOpts = [
|
|||
status: TaskStatus.Complete,
|
||||
name: 'taskOrder.status.Complete',
|
||||
},
|
||||
{
|
||||
status: TaskStatus.Restart,
|
||||
name: 'taskOrder.status.Restart',
|
||||
},
|
||||
];
|
||||
|
||||
export const pageTabs = [
|
||||
|
|
@ -89,6 +93,11 @@ export const taskStatusOrderToggle = [
|
|||
icon: 'mdi-file-remove-outline',
|
||||
color: 'negative',
|
||||
},
|
||||
{
|
||||
value: TaskStatus.Restart,
|
||||
icon: 'mdi-file-remove-outline',
|
||||
color: 'negative',
|
||||
},
|
||||
];
|
||||
|
||||
export const column = [
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ defineProps<{
|
|||
<span>{{ branch.webUrl }}</span>
|
||||
</article>
|
||||
<article>
|
||||
<b>ลูกค้า</b>
|
||||
<b>{{ $t('general.agencyAddress') }}</b>
|
||||
<span>
|
||||
{{
|
||||
formatAddress({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts" setup>
|
||||
// NOTE: Import stores
|
||||
import { dateFormat } from 'src/utils/datetime';
|
||||
import { dateFormatJS } from 'src/utils/datetime';
|
||||
|
||||
// NOTE Import Types
|
||||
import {
|
||||
|
|
@ -19,8 +19,8 @@ defineProps<{
|
|||
contactTel?: string;
|
||||
contactUrl?: string;
|
||||
email?: string;
|
||||
receivedDate?: Date | string;
|
||||
deliveryDate?: Date | string;
|
||||
acceptedAt?: Date | string;
|
||||
submittedAt?: Date | string;
|
||||
status?: UserTaskStatus;
|
||||
}>();
|
||||
</script>
|
||||
|
|
@ -96,7 +96,11 @@ defineProps<{
|
|||
:label="$t('taskOrder.workStartDate')"
|
||||
>
|
||||
<template #value>
|
||||
{{ receivedDate ? dateFormat(receivedDate) : '-' }}
|
||||
{{
|
||||
acceptedAt
|
||||
? dateFormatJS({ date: acceptedAt, withTime: true })
|
||||
: '-'
|
||||
}}
|
||||
</template>
|
||||
</DataDisplay>
|
||||
|
||||
|
|
@ -105,7 +109,11 @@ defineProps<{
|
|||
:label="$t('taskOrder.workSubmissionDate')"
|
||||
>
|
||||
<template #value>
|
||||
{{ deliveryDate ? dateFormat(deliveryDate) : '-' }}
|
||||
{{
|
||||
submittedAt
|
||||
? dateFormatJS({ date: submittedAt, withTime: true })
|
||||
: '-'
|
||||
}}
|
||||
</template>
|
||||
</DataDisplay>
|
||||
|
||||
|
|
@ -121,6 +129,7 @@ defineProps<{
|
|||
Pending: 'waitReceive',
|
||||
Accept: 'receiveTask',
|
||||
Submit: 'sentTask',
|
||||
Restart: 'status.Restart',
|
||||
}[status] ?? 'waitReceive'
|
||||
}`,
|
||||
)
|
||||
|
|
@ -130,6 +139,7 @@ defineProps<{
|
|||
Pending: '--info-bg',
|
||||
Accept: '--info-bg',
|
||||
Submit: '--positive-bg',
|
||||
Restart: '--negative-bg',
|
||||
}[status] ?? '--info-bg'
|
||||
"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -467,6 +467,7 @@ async function getFileList(taskId: string) {
|
|||
async function remove(taskId: string, n: string) {
|
||||
dialogWarningClose(t, {
|
||||
message: t('dialog.message.confirmDelete'),
|
||||
actionText: t('dialog.action.ok'),
|
||||
action: async () => {
|
||||
const res = await taskOrderStore.delAttachment({
|
||||
parentId: taskId,
|
||||
|
|
@ -876,6 +877,16 @@ watch([currentFormData.value.taskStatus], () => {
|
|||
(l) => l.userId === v.responsibleUser.id,
|
||||
)?.userTaskStatus || UserTaskStatus.Pending
|
||||
"
|
||||
:accepted-at="
|
||||
fullTaskOrder?.userTask.find(
|
||||
(l) => l.userId === v.responsibleUser.id,
|
||||
)?.acceptedAt
|
||||
"
|
||||
:submitted-at="
|
||||
fullTaskOrder?.userTask.find(
|
||||
(l) => l.userId === v.responsibleUser.id,
|
||||
)?.submittedAt
|
||||
"
|
||||
>
|
||||
<template #product>
|
||||
<FormGroupHead>
|
||||
|
|
@ -977,12 +988,21 @@ watch([currentFormData.value.taskStatus], () => {
|
|||
>
|
||||
<template #append="{ props: subProps }">
|
||||
<TaskStatusComponent
|
||||
:key="subProps.row.id"
|
||||
:no-action="view !== TaskOrderStatus.Validate"
|
||||
type="order"
|
||||
:readonly="
|
||||
fullTaskOrder?.userTask.find(
|
||||
(l) => l.userId === v.responsibleUser.id,
|
||||
)?.userTaskStatus !== UserTaskStatus.Submit
|
||||
(() => {
|
||||
const _userStatus =
|
||||
fullTaskOrder?.userTask.find(
|
||||
(l) => l.userId === v.responsibleUser.id,
|
||||
)?.userTaskStatus;
|
||||
console.log(_userStatus);
|
||||
return (
|
||||
_userStatus !== UserTaskStatus.Submit &&
|
||||
_userStatus !== UserTaskStatus.Restart
|
||||
);
|
||||
})()
|
||||
"
|
||||
:status="subProps.row.taskStatus"
|
||||
@click-failed="
|
||||
|
|
@ -1066,27 +1086,40 @@ watch([currentFormData.value.taskStatus], () => {
|
|||
solid
|
||||
/>
|
||||
</template>
|
||||
|
||||
<SaveButton
|
||||
v-if="state.mode !== 'create' && view === TaskOrderStatus.Validate"
|
||||
:disabled="
|
||||
!fullTaskOrder?.taskList.every(
|
||||
(t) =>
|
||||
t.taskStatus === TaskStatus.Complete ||
|
||||
t.taskStatus === TaskStatus.Redo ||
|
||||
t.taskStatus === TaskStatus.Canceled,
|
||||
) || fullTaskOrder.taskOrderStatus === TaskOrderStatus.Complete
|
||||
!fullTaskOrder?.taskList.some((t) =>
|
||||
[
|
||||
TaskStatus.Complete,
|
||||
TaskStatus.Redo,
|
||||
TaskStatus.Canceled,
|
||||
TaskStatus.Validate,
|
||||
].includes(t.taskStatus),
|
||||
) || fullTaskOrder?.taskOrderStatus === TaskOrderStatus.Complete
|
||||
"
|
||||
@click="
|
||||
dialogWarningClose(t, {
|
||||
message: t('dialog.message.confirmValidate'),
|
||||
message: $t(
|
||||
`${
|
||||
fullTaskOrder?.taskList.some(
|
||||
(t) =>
|
||||
t.taskStatus === TaskStatus.Pending ||
|
||||
t.taskStatus === TaskStatus.InProgress ||
|
||||
t.taskStatus === TaskStatus.Restart,
|
||||
)
|
||||
? 'dialog.message.confirmEndWorkWarning'
|
||||
: 'dialog.message.confirmEndWork'
|
||||
}`,
|
||||
),
|
||||
actionText: $t('dialog.action.ok'),
|
||||
action: async () => {
|
||||
await completeValidate();
|
||||
},
|
||||
cancel: () => {},
|
||||
})
|
||||
"
|
||||
:label="$t('taskOrder.confirmValidate')"
|
||||
:label="$t('taskOrder.confirmEndWork')"
|
||||
icon="mdi-check"
|
||||
solid
|
||||
></SaveButton>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { computed, onMounted, ref, watch } from 'vue';
|
|||
import { getUserId } from 'src/services/keycloak';
|
||||
|
||||
// NOTE: Import Components
|
||||
import { SaveButton, MainButton } from 'src/components/button';
|
||||
import { SaveButton } from 'src/components/button';
|
||||
import { StateButton } from 'components/button';
|
||||
import InfoMessengerExpansion from '../expansion/receive/InfoMessengerExpansion.vue';
|
||||
import InfoProductExpansion from '../expansion/receive/InfoProductExpansion.vue';
|
||||
|
|
@ -61,6 +61,7 @@ const statusTabForm = ref<
|
|||
},
|
||||
]);
|
||||
const failedDialog = ref(false);
|
||||
const failedDialogReadonly = ref(false);
|
||||
const taskStatusRecords = ref<
|
||||
{
|
||||
requestWorkId: string;
|
||||
|
|
@ -72,6 +73,7 @@ const taskStatusRecords = ref<
|
|||
>([]);
|
||||
const selectedEmployee = ref<
|
||||
(RequestWork & {
|
||||
taskStatus: TaskStatus;
|
||||
_template?: {
|
||||
id: string;
|
||||
templateName: string;
|
||||
|
|
@ -207,15 +209,6 @@ let taskListGroup = computed(() => {
|
|||
// NOTE: Function
|
||||
async function sendTask() {
|
||||
if (!fullTaskOrder.value) return;
|
||||
if (
|
||||
!fullTaskOrder.value.taskList.every(
|
||||
(t) =>
|
||||
t.taskStatus === TaskStatus.Success ||
|
||||
t.taskStatus === TaskStatus.Failed ||
|
||||
t.taskStatus === TaskStatus.Canceled,
|
||||
)
|
||||
)
|
||||
return;
|
||||
await useTaskOrderStore().submitTaskOrder(
|
||||
fullTaskOrder.value.id,
|
||||
getUserId(),
|
||||
|
|
@ -477,6 +470,20 @@ watch([currentFormData.value.taskStatus], () => {
|
|||
fullTaskOrder?.taskList[0].requestWorkStep.responsibleUserId,
|
||||
)?.userTaskStatus || UserTaskStatus.Pending
|
||||
"
|
||||
:accepted-at="
|
||||
fullTaskOrder.userTask.find(
|
||||
(l) =>
|
||||
l.userId ===
|
||||
fullTaskOrder?.taskList[0].requestWorkStep.responsibleUserId,
|
||||
)?.acceptedAt
|
||||
"
|
||||
:submitted-at="
|
||||
fullTaskOrder.userTask.find(
|
||||
(l) =>
|
||||
l.userId ===
|
||||
fullTaskOrder?.taskList[0].requestWorkStep.responsibleUserId,
|
||||
)?.submittedAt
|
||||
"
|
||||
/>
|
||||
|
||||
<InfoProductExpansion
|
||||
|
|
@ -633,6 +640,15 @@ watch([currentFormData.value.taskStatus], () => {
|
|||
},
|
||||
];
|
||||
failedDialog = true;
|
||||
failedDialogReadonly =
|
||||
fullTaskOrder?.userTask.find(
|
||||
(l) =>
|
||||
l.userId ===
|
||||
fullTaskOrder?.taskList[0].requestWorkStep
|
||||
.responsibleUserId,
|
||||
)?.userTaskStatus === UserTaskStatus.Submit
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
"
|
||||
@change-status="
|
||||
|
|
@ -654,6 +670,7 @@ watch([currentFormData.value.taskStatus], () => {
|
|||
</q-expansion-item>
|
||||
|
||||
<FailRemarkDialog
|
||||
:readonly="failedDialogReadonly"
|
||||
:fail-task-option="list"
|
||||
v-model:open="failedDialog"
|
||||
v-model:set-task-status-list="taskStatusRecords"
|
||||
|
|
@ -688,6 +705,7 @@ watch([currentFormData.value.taskStatus], () => {
|
|||
</article>
|
||||
|
||||
<!-- SEC: footer -->
|
||||
|
||||
<footer
|
||||
v-if="fullTaskOrder.taskOrderStatus !== TaskOrderStatus.Pending"
|
||||
class="surface-1 q-pa-md full-width"
|
||||
|
|
@ -695,16 +713,22 @@ watch([currentFormData.value.taskStatus], () => {
|
|||
<nav class="row justify-end">
|
||||
<SaveButton
|
||||
:disabled="
|
||||
!fullTaskOrder.taskList.every(
|
||||
fullTaskOrder.taskList.some(
|
||||
(t) =>
|
||||
t.taskStatus === TaskStatus.Success ||
|
||||
t.taskStatus === TaskStatus.Failed ||
|
||||
t.taskStatus === TaskStatus.Canceled,
|
||||
)
|
||||
t.taskStatus === TaskStatus.Pending ||
|
||||
t.taskStatus === TaskStatus.InProgress ||
|
||||
t.taskStatus === TaskStatus.Restart,
|
||||
) ||
|
||||
fullTaskOrder?.userTask.find(
|
||||
(l) =>
|
||||
l.userId ===
|
||||
fullTaskOrder?.taskList[0].requestWorkStep.responsibleUserId,
|
||||
)?.userTaskStatus !== UserTaskStatus.Accept
|
||||
"
|
||||
@click="
|
||||
dialogWarningClose($t, {
|
||||
message: $t('dialog.message.confirmSending'),
|
||||
actionText: $t('dialog.action.ok'),
|
||||
action: async () => {
|
||||
await sendTask();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export enum TaskOrderStatus {
|
|||
Complete = 'Complete',
|
||||
Accept = 'Accept', // messenger only
|
||||
Submit = 'Submit', // messenger only
|
||||
Restart = 'Restart',
|
||||
}
|
||||
|
||||
export enum TaskStatus {
|
||||
|
|
@ -23,6 +24,7 @@ export enum TaskStatus {
|
|||
Validate = 'Validate',
|
||||
Complete = 'Complete',
|
||||
Canceled = 'Canceled',
|
||||
Restart = 'Restart',
|
||||
}
|
||||
|
||||
export interface TaskOrder {
|
||||
|
|
@ -61,6 +63,8 @@ export interface UserTask {
|
|||
taskOrderId: string;
|
||||
userId: string;
|
||||
userTaskStatus: UserTaskStatus;
|
||||
acceptedAt?: Date | string;
|
||||
submittedAt?: Date | string;
|
||||
}
|
||||
|
||||
export interface Institution {
|
||||
|
|
@ -196,6 +200,7 @@ export enum UserTaskStatus {
|
|||
Pending = 'Pending',
|
||||
Accept = 'Accept',
|
||||
Submit = 'Submit',
|
||||
Restart = 'Restart',
|
||||
}
|
||||
|
||||
export interface SetTaskStatusPayload {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ import { getRole } from 'src/services/keycloak';
|
|||
|
||||
import GlobalDialog from 'components/GlobalDialog.vue';
|
||||
import DialogDuplicateData from 'components/DialogDuplicateData.vue';
|
||||
import useOptionStore from '../options';
|
||||
import { CustomerBranch } from '../customer/types';
|
||||
import { CustomerBranchRelation } from '../quotations';
|
||||
import { Employee } from '../employee/types';
|
||||
|
||||
export const baseUrl = import.meta.env.VITE_API_BASE_URL;
|
||||
|
||||
|
|
@ -53,6 +57,7 @@ export function dialogWarningClose(
|
|||
t: ComposerTranslation,
|
||||
opts: {
|
||||
message?: string;
|
||||
actionText?: string;
|
||||
action?: (...args: unknown[]) => unknown;
|
||||
cancel?: (...args: unknown[]) => unknown;
|
||||
},
|
||||
|
|
@ -61,7 +66,7 @@ export function dialogWarningClose(
|
|||
color: 'warning',
|
||||
icon: 'mdi-alert',
|
||||
title: t('form.warning.title'),
|
||||
actionText: t('dialog.action.close'),
|
||||
actionText: opts.actionText || t('dialog.action.close'),
|
||||
message: opts.message || t('dialog.message.warningClose'),
|
||||
action: async () => {
|
||||
if (opts.action) opts.action();
|
||||
|
|
@ -542,3 +547,42 @@ export function changeMode(mode: string) {
|
|||
export function capitalizeFirstLetter(val: string) {
|
||||
return String(val).charAt(0).toUpperCase() + String(val).slice(1);
|
||||
}
|
||||
|
||||
export function getCustomerName(
|
||||
record: CustomerBranch | CustomerBranchRelation,
|
||||
opts?: {
|
||||
locale?: string;
|
||||
noCode?: boolean;
|
||||
},
|
||||
) {
|
||||
const customer = record;
|
||||
|
||||
return (
|
||||
{
|
||||
['CORP']: {
|
||||
['eng']: customer.registerNameEN,
|
||||
['tha']: customer.registerName,
|
||||
}[opts?.locale || 'eng'],
|
||||
['PERS']:
|
||||
{
|
||||
['eng']: `${useOptionStore().mapOption(customer?.namePrefix || '')} ${customer?.firstNameEN} ${customer?.lastNameEN}`,
|
||||
['tha']: `${useOptionStore().mapOption(customer?.namePrefix || '')} ${customer?.firstName} ${customer?.lastName}`,
|
||||
}[opts?.locale || 'eng'] || '-',
|
||||
}[customer.customer.customerType] +
|
||||
(opts?.noCode ? '' : ' ' + `(${customer.code})`)
|
||||
);
|
||||
}
|
||||
|
||||
export function getEmployeeName(
|
||||
record: Employee,
|
||||
opts?: {
|
||||
locale?: string;
|
||||
},
|
||||
) {
|
||||
const employee = record;
|
||||
|
||||
return {
|
||||
['eng']: `${useOptionStore().mapOption(employee.namePrefix)} ${employee.firstNameEN} ${employee.lastNameEN}`,
|
||||
['tha']: `${useOptionStore().mapOption(employee.namePrefix)} ${employee.firstName} ${employee.lastName}`,
|
||||
}[opts?.locale || 'eng'];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ export function dateFormatJS(opts: {
|
|||
noDay?: boolean;
|
||||
noMonth?: boolean;
|
||||
noYear?: boolean;
|
||||
}) {
|
||||
withTime?: boolean;
|
||||
}): string {
|
||||
const dt = opts.date ? new Date(opts.date) : new Date();
|
||||
|
||||
const { locale } = i18n.global;
|
||||
|
|
@ -35,6 +36,17 @@ export function dateFormatJS(opts: {
|
|||
opts.timeStyle = opts.timeStyle || 'short';
|
||||
}
|
||||
|
||||
let timeText = opts.withTime
|
||||
? ' ' +
|
||||
dateFormatJS({
|
||||
date: opts.date,
|
||||
timeOnly: true,
|
||||
timeStyle: opts.timeStyle,
|
||||
})
|
||||
: '';
|
||||
|
||||
if (timeText) opts.timeStyle = undefined;
|
||||
|
||||
let formatted = new Intl.DateTimeFormat(opts.locale, {
|
||||
day: opts.noDay ? undefined : opts.dayStyle || 'numeric',
|
||||
month: opts.noMonth ? undefined : opts.monthStyle || 'short',
|
||||
|
|
@ -46,11 +58,13 @@ export function dateFormatJS(opts: {
|
|||
case Lang.Thai:
|
||||
case 'th-TH':
|
||||
case 'th-Th':
|
||||
return formatted.replace(/(\d{4})/, (year) =>
|
||||
(parseInt(year) - 543).toString(),
|
||||
return (
|
||||
formatted.replace(/(\d{4})/, (year) =>
|
||||
(parseInt(year) - 543).toString(),
|
||||
) + timeText
|
||||
);
|
||||
default:
|
||||
return formatted;
|
||||
return formatted + timeText;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue