Merge branch 'develop'

This commit is contained in:
Methapon2001 2025-01-10 16:49:09 +07:00
commit 55c15585f8
22 changed files with 403 additions and 115 deletions

View file

@ -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();
},
});
},
);

View file

@ -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'),
]"
/>

View 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>

View file

@ -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',

View file

@ -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: 'ยืนยัน',

View file

@ -4630,7 +4630,7 @@ const emptyCreateDialog = ref(false);
"
class="q-mb-xl"
/>
<FormPerso
<FormPerson
id="drawer-form-personal"
prefix-id="drawer-info-employee"
dense

View file

@ -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>

View file

@ -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('');

View file

@ -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"

View file

@ -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

View file

@ -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]"

View file

@ -816,6 +816,7 @@ function goToQuotation(
"
/>
<PropertiesExpansion
:request-list-data="data"
:id="value.id"
:readonly="
data.requestDataStatus === RequestDataStatus.Canceled

View file

@ -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'),

View file

@ -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,

View file

@ -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 = [

View file

@ -69,7 +69,7 @@ defineProps<{
<span>{{ branch.webUrl }}</span>
</article>
<article>
<b>กค</b>
<b>{{ $t('general.agencyAddress') }}</b>
<span>
{{
formatAddress({

View file

@ -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'
"
/>

View file

@ -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>

View file

@ -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();
},

View file

@ -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 {

View file

@ -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'];
}

View file

@ -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;
}
}