Date: Fri, 11 Apr 2025 11:43:52 +0700
Subject: [PATCH 055/331] feat: add VAT parameter to calcPrice function and
update related calculations
---
src/pages/11_credit-note/document-view/MainPage.vue | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/pages/11_credit-note/document-view/MainPage.vue b/src/pages/11_credit-note/document-view/MainPage.vue
index 73024f36..e15fe13d 100644
--- a/src/pages/11_credit-note/document-view/MainPage.vue
+++ b/src/pages/11_credit-note/document-view/MainPage.vue
@@ -248,6 +248,7 @@ function calcPricePerUnit(product: RequestWork['productService']['product']) {
function calcPrice(
product: RequestWork['productService']['product'],
amount: number,
+ vat: number = 0,
) {
const pricePerUnit = agentPrice.value ? product.agentPrice : product.price;
@@ -256,7 +257,8 @@ function calcPrice(
: pricePerUnit;
const priceDiscountNoVat = priceNoVat * amount - 0;
- const rawVatTotal = priceDiscountNoVat * (config.value?.vat || 0.07);
+ const rawVatTotal =
+ vat === 0 ? 0 : priceDiscountNoVat * (config.value?.vat || 0.07);
return precisionRound(priceNoVat * amount + rawVatTotal);
}
@@ -346,7 +348,7 @@ function closeAble() {
{{
formatNumberDecimal(
- calcPrice(v.product.product, v.list.length),
+ calcPrice(v.product.product, v.list.length, v.product.vat),
2,
)
}}
@@ -431,7 +433,7 @@ function closeAble() {
class="column set-width bg-color full-height"
style="padding: 12px"
>
- ({{ ThaiBahtText(summaryPrice.finalPrice) }})
+ ({{ ThaiBahtText(precisionRound(summaryPrice.finalPrice)) }})
Date: Fri, 11 Apr 2025 13:24:08 +0700
Subject: [PATCH 056/331] refactor: add new column
---
src/i18n/eng.ts | 9 ++-------
src/i18n/tha.ts | 1 +
.../08_request-list/TableRequestList.vue | 20 +++++++++++++++++++
src/pages/08_request-list/constants.ts | 18 +++++++++++++++++
src/stores/request-list/types.ts | 1 +
5 files changed, 42 insertions(+), 7 deletions(-)
diff --git a/src/i18n/eng.ts b/src/i18n/eng.ts
index 241621bb..49950acd 100644
--- a/src/i18n/eng.ts
+++ b/src/i18n/eng.ts
@@ -477,7 +477,6 @@ export default {
powerOfAttorney: 'Power of Attorney',
others: 'Others',
},
-
employer: 'Employer',
employerLegalEntity: 'Legal Entity',
employerNaturalPerson: 'Natrual Person',
@@ -499,15 +498,12 @@ export default {
religion: 'Religion',
issueDate: 'Issue Date',
passportExpiryDate: 'Passport Expiry Date',
-
ownerName: 'Customer Name',
firstName: 'First Name ',
lastName: 'Last Name ',
firstNameEN: 'First Name in English',
lastNameEN: 'Last Name in English',
-
cardNumber: 'ID Card Number',
-
prefixName: 'Prefix',
legalPersonNo: 'Legal Entity Registration Number',
registerName: 'Company Name',
@@ -515,7 +511,6 @@ export default {
registerDate: 'Registered On',
registerCompanyName: 'Registered Name',
authorizedCapital: 'Authorized Capital',
-
workplace: 'Workplace',
workplaceEN: 'Workplace (EN)',
address: 'Address',
@@ -523,7 +518,6 @@ export default {
branchCode: 'Branch Code',
customerCode: 'Employer Code',
legalPersonCode: 'Legal Entity Code',
-
codeAbbrev: 'Company Abbreviation',
codeNumber: 'Company Number',
registeredBranch: 'Registered Branch',
@@ -941,8 +935,9 @@ export default {
localEmployee: 'Local Employee',
nonLocalEmployee: 'Non Local Employee',
noWorkflowTemplate: 'A workflow template has not been selected.',
-
salesRepresentative: 'Sales Representative',
+
+ dataOffice: 'Employment Office District',
ref: 'Reference',
action: {
title: 'Action',
diff --git a/src/i18n/tha.ts b/src/i18n/tha.ts
index f4837cc0..de4bebbd 100644
--- a/src/i18n/tha.ts
+++ b/src/i18n/tha.ts
@@ -932,6 +932,7 @@ export default {
nonLocalEmployee: 'พนักงานนอกพื้นที่',
noWorkflowTemplate: 'คุณไม่ได้เลือกแม่แบบขั้นตอนการทำงาน',
salesRepresentative: 'พนักงานขาย',
+ dataOffice: 'สำนักงานเขตจัดหางาน',
ref: 'อ้างอิง',
action: {
title: 'จัดการ',
diff --git a/src/pages/08_request-list/TableRequestList.vue b/src/pages/08_request-list/TableRequestList.vue
index 99706193..e0ae445e 100644
--- a/src/pages/08_request-list/TableRequestList.vue
+++ b/src/pages/08_request-list/TableRequestList.vue
@@ -13,6 +13,7 @@ import useOptionStore from 'src/stores/options';
import KebabAction from 'src/components/shared/KebabAction.vue';
import { CreatedBy } from 'src/stores/types';
+import { dateFormatJS } from 'src/utils/datetime';
const props = withDefaults(
defineProps<{
@@ -168,6 +169,25 @@ function getEmployeeName(
{{ getEmployeeName(props.row, { locale: $i18n.locale }) || '-' }}
+
+
+ {{
+ props.row.employee.employeePassport.length !== 0
+ ? props.row.employee.employeePassport[0].number
+ : '-'
+ }}
+
+
+ {{
+ $i18n.locale === 'eng'
+ ? props.row.dataOffice.nameEN
+ : props.row.dataOffice.name
+ }}
+
+
+ {{ dateFormatJS({ date: props.row.createdAt }) }}
+
+
{{ props.row.quotation.code || '-' }}
diff --git a/src/pages/08_request-list/constants.ts b/src/pages/08_request-list/constants.ts
index 2534aea5..c71aac2e 100644
--- a/src/pages/08_request-list/constants.ts
+++ b/src/pages/08_request-list/constants.ts
@@ -28,6 +28,24 @@ export const column = [
label: 'customer.employee',
field: 'employee',
},
+ {
+ name: 'employeePassport',
+ align: 'center',
+ label: 'customerEmployee.form.passportNo',
+ field: 'employeePassport',
+ },
+ {
+ name: 'dataOffice',
+ align: 'center',
+ label: 'requestList.dataOffice',
+ field: 'dataOffice',
+ },
+ {
+ name: 'createdAt',
+ align: 'center',
+ label: 'general.createdAt',
+ field: 'createdAt',
+ },
{
name: 'quotationCode',
diff --git a/src/stores/request-list/types.ts b/src/stores/request-list/types.ts
index 8d7f5d08..e3d8cd57 100644
--- a/src/stores/request-list/types.ts
+++ b/src/stores/request-list/types.ts
@@ -28,6 +28,7 @@ export type RequestData = {
requestWork: RequestWork[];
requestDataStatus: RequestDataStatus;
+ dataOffice: { name: string; nameEN: string };
};
export enum RequestDataStatus {
From 0efe78a37a7401de6c118cf36bfe14821fdd01d9 Mon Sep 17 00:00:00 2001
From: Thanaphon Frappet
Date: Fri, 11 Apr 2025 13:34:21 +0700
Subject: [PATCH 057/331] refactor: visible columns
---
src/pages/08_request-list/TableRequestList.vue | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/pages/08_request-list/TableRequestList.vue b/src/pages/08_request-list/TableRequestList.vue
index e0ae445e..9ce78e72 100644
--- a/src/pages/08_request-list/TableRequestList.vue
+++ b/src/pages/08_request-list/TableRequestList.vue
@@ -170,21 +170,24 @@ function getEmployeeName(
{{ getEmployeeName(props.row, { locale: $i18n.locale }) || '-' }}
-
+
{{
props.row.employee.employeePassport.length !== 0
? props.row.employee.employeePassport[0].number
: '-'
}}
-
+
{{
$i18n.locale === 'eng'
? props.row.dataOffice.nameEN
: props.row.dataOffice.name
}}
-
+
{{ dateFormatJS({ date: props.row.createdAt }) }}
From 586fbed4e382c4fa071c01dece8d801755a2a5e4 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Fri, 11 Apr 2025 15:50:18 +0700
Subject: [PATCH 058/331] refactor: remove console.log from
openRequestListDialog function
---
src/pages/08_request-list/MainPage.vue | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/pages/08_request-list/MainPage.vue b/src/pages/08_request-list/MainPage.vue
index c615b4da..e316e376 100644
--- a/src/pages/08_request-list/MainPage.vue
+++ b/src/pages/08_request-list/MainPage.vue
@@ -142,7 +142,6 @@ async function openRequestListDialog() {
incomplete: true,
});
- console.log(ret);
if (ret) {
requestListActionData.value = ret.result;
}
From d909be2fc44b4d7a5fa786417cc183c50f087ca4 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Fri, 11 Apr 2025 15:50:24 +0700
Subject: [PATCH 059/331] feat: add navigation to customer and employee details
from request list
---
src/pages/03_customer-management/MainPage.vue | 32 ++++++++++++
src/pages/08_request-list/RequestListView.vue | 22 ++++++++
.../08_request-list/TableRequestList.vue | 51 ++++++++++++++++---
3 files changed, 98 insertions(+), 7 deletions(-)
diff --git a/src/pages/03_customer-management/MainPage.vue b/src/pages/03_customer-management/MainPage.vue
index fd2647c7..cee0de34 100644
--- a/src/pages/03_customer-management/MainPage.vue
+++ b/src/pages/03_customer-management/MainPage.vue
@@ -142,6 +142,14 @@ async function init() {
gridView.value = $q.screen.lt.md ? true : false;
+ if (route.query.tab === 'customer') {
+ currentTab.value = 'employer';
+ if (route.query.id) openSpecificCustomer(route.query.id as string);
+ } else if (route.query.tab === 'employee') {
+ currentTab.value = 'employee';
+ if (route.query.id) openSpecificEmployee(route.query.id as string);
+ }
+
if (route.name === 'CustomerManagement') await fetchListCustomer(true);
if (
@@ -490,6 +498,30 @@ async function fetchImageList(
return res;
}
+async function openSpecificCustomer(id: string) {
+ await customerFormStore.assignFormData(id);
+ await fetchImageList(
+ id,
+ customerFormData.value.selectedImage || '',
+ 'customer',
+ );
+ customerFormState.value.branchIndex = -1;
+ customerFormState.value.drawerModal = true;
+ customerFormState.value.editCustomerId = id;
+ customerFormState.value.dialogType = 'info';
+}
+
+async function openSpecificEmployee(id: string) {
+ await employeeFormStore.assignFormDataEmployee(id);
+ await fetchImageList(
+ id,
+ currentFromDataEmployee.value.selectedImage || '',
+ 'employee',
+ );
+ employeeFormState.value.dialogType = 'info';
+ employeeFormState.value.drawerModal = true;
+}
+
// TODO: When in employee form, if select address same as customer then auto fill
watch(
diff --git a/src/pages/08_request-list/RequestListView.vue b/src/pages/08_request-list/RequestListView.vue
index 94343725..9327f419 100644
--- a/src/pages/08_request-list/RequestListView.vue
+++ b/src/pages/08_request-list/RequestListView.vue
@@ -438,6 +438,24 @@ async function submitRejectCancel() {
pageState.rejectCancelDialog = false;
}
}
+
+function toCustomer(customer: RequestData['quotation']['customerBranch']) {
+ const url = new URL(
+ `/customer-management?tab=customer&id=${customer.customerId}`,
+ window.location.origin,
+ );
+
+ window.open(url.toString(), '_blank');
+}
+
+function toEmployee(employee: RequestData['employee']) {
+ const url = new URL(
+ `/customer-management?tab=employee&id=${employee.id}`,
+ window.location.origin,
+ );
+
+ window.open(url.toString(), '_blank');
+}
@@ -701,6 +719,7 @@ async function submitRejectCancel() {
}"
>
- {{
- getCustomerName(props.row, {
- noCode: true,
- locale: $i18n.locale,
- }) || '-'
- }}
+
+ {{
+ getCustomerName(props.row, {
+ noCode: true,
+ locale: $i18n.locale,
+ }) || '-'
+ }}
+ {{
+ getCustomerName(props.row, {
+ noCode: true,
+ locale: $i18n.locale,
+ }) || '-'
+ }}
+
- {{ getEmployeeName(props.row, { locale: $i18n.locale }) || '-' }}
+
+ {{ getEmployeeName(props.row, { locale: $i18n.locale }) || '-' }}
+
From 82f48a4b8092c1837b6abb55ad8a922986886b41 Mon Sep 17 00:00:00 2001
From: Thanaphon Frappet
Date: Fri, 11 Apr 2025 17:47:23 +0700
Subject: [PATCH 060/331] feat: copy
---
src/components/button/PasteButton.vue | 32 +++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
create mode 100644 src/components/button/PasteButton.vue
diff --git a/src/components/button/PasteButton.vue b/src/components/button/PasteButton.vue
new file mode 100644
index 00000000..c06a2833
--- /dev/null
+++ b/src/components/button/PasteButton.vue
@@ -0,0 +1,32 @@
+
+
+
+ $emit('click', e)"
+ v-bind="{ ...$props, ...$attrs }"
+ :icon="icon || 'mdi-file-replace'"
+ color="207 96% 32%"
+ :title="iconOnly ? $t('general.paste') : undefined"
+ >
+ {{ label || $t('general.paste') }}
+ {{ amount && amount > 0 ? `(${amount})` : '' }}
+
+
From 1d5f77f3a6e017ec247dd2683d46e0a479faa485 Mon Sep 17 00:00:00 2001
From: Thanaphon Frappet
Date: Fri, 11 Apr 2025 17:59:40 +0700
Subject: [PATCH 061/331] feat: copy goodbey
---
src/components/button/index.ts | 1 +
src/components/shared/KebabAction.vue | 23 +++++++
src/i18n/eng.ts | 6 ++
src/i18n/tha.ts | 5 ++
src/pages/04_product-service/MainPage.vue | 84 ++++++++++++++++++++++-
5 files changed, 118 insertions(+), 1 deletion(-)
diff --git a/src/components/button/index.ts b/src/components/button/index.ts
index d9b59582..9f3367ef 100644
--- a/src/components/button/index.ts
+++ b/src/components/button/index.ts
@@ -14,3 +14,4 @@ export { default as PrintButton } from './PrintButton.vue';
export { default as StateButton } from './StateButton.vue';
export { default as NextButton } from './NextButton.vue';
export { default as ImportButton } from './ImportButton.vue';
+export { default as PasteButton } from './PasteButton.vue';
diff --git a/src/components/shared/KebabAction.vue b/src/components/shared/KebabAction.vue
index 4dd095db..7bea6b36 100644
--- a/src/components/shared/KebabAction.vue
+++ b/src/components/shared/KebabAction.vue
@@ -16,6 +16,7 @@ const props = withDefaults(
useUpload?: boolean;
useCancel?: boolean;
useRejectCancel?: boolean;
+ useCopy?: boolean;
disableCancel?: boolean;
disableDelete?: boolean;
}>(),
@@ -31,6 +32,7 @@ defineEmits<{
(e: 'link'): void;
(e: 'upload'): void;
(e: 'delete'): void;
+ (e: 'copy'): void;
(e: 'cancel'): void;
(e: 'rejectCancel'): void;
(e: 'changeStatus'): void;
@@ -172,6 +174,27 @@ watch(
+ $emit('copy')"
+ >
+
+
+ {{ $t('general.copy') }}
+
+
+
();
const { workNameItems } = storeToRefs(productServiceStore);
const allStat = ref<{ mode: string; count: number }[]>([]);
const stat = ref<
@@ -1831,6 +1837,67 @@ function handleSubmitSameWorkflow() {
);
}
+async function paste() {
+ if (
+ !!currentCopy.value &&
+ currentCopy.value.type === 'service' &&
+ !!currentCopy.value.id
+ )
+ dialogWarningClose(t, {
+ message: t('dialog.message.warningPaste'),
+ action: async () => {
+ const res = await fetchListServiceById(currentCopy.value.id);
+ if (res) {
+ formService.value = {
+ code: res.code,
+ name: res.name,
+ detail: res.detail,
+ attributes: res.attributes,
+ work: res.work.map((v) => ({
+ id: v.id,
+ name: v.name,
+ attributes: v.attributes,
+ product: v.productOnWork.map((productOnWorkItem) => ({
+ id: productOnWorkItem.product.id,
+ installmentNo: productOnWorkItem.installmentNo,
+ stepCount: productOnWorkItem.stepCount,
+ })),
+ })),
+ status: res.status,
+ productGroupId: res.productGroupId,
+ selectedImage: res.selectedImage,
+ installments: res.installments,
+ };
+
+ workItems.value = res.work.map((item) => {
+ return {
+ id: item.id,
+ name: item.name,
+ attributes: item.attributes,
+ product: item.productOnWork.map((productOnWorkItem) => {
+ return {
+ ...productOnWorkItem.product,
+ nameEn: productOnWorkItem.product.name,
+ installmentNo: productOnWorkItem.installmentNo,
+ };
+ }),
+ };
+ });
+ }
+ },
+ });
+ else {
+ dialog({
+ color: 'warning',
+ icon: 'mdi-alert',
+ title: t('form.warning.title'),
+ actionText: t('dialog.action.ok'),
+ message: t('dialog.message.warningCopyEmpty'),
+ action: async () => {},
+ });
+ }
+}
+
watch(
() => formService.value.attributes.workflowId,
async (a, b) => {
@@ -3127,8 +3194,18 @@ watch(
"
/>
{
+ notify('create', $t('dialog.message.copy'));
+ currentCopy = {
+ id: props.row.id,
+ type: productAndServiceTab,
+ };
+ }
+ "
@view="
async () => {
if (props.row.type === 'product') {
@@ -4422,6 +4499,11 @@ watch(
@click="serviceTreeView = false"
/>
+ paste()"
+ />
From 08b0dcbce082710068aa65c8b8cb73d96bc329a7 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 17 Apr 2025 13:53:14 +0700
Subject: [PATCH 062/331] feat: add new translations for date range and
document status
---
src/i18n/eng.ts | 18 ++++++++++++++++++
src/i18n/tha.ts | 18 ++++++++++++++++++
2 files changed, 36 insertions(+)
diff --git a/src/i18n/eng.ts b/src/i18n/eng.ts
index e0278ea0..00d10ae3 100644
--- a/src/i18n/eng.ts
+++ b/src/i18n/eng.ts
@@ -156,6 +156,9 @@ export default {
nativeLanguage: '{msg} Native Language',
copy: 'Copy',
paste: 'Paste',
+ period: 'Period',
+ documentStatus: 'Document Status',
+ advanceSearch: 'Advance Search',
},
menu: {
@@ -1486,4 +1489,19 @@ export default {
type: 'Type',
},
},
+
+ dateRange: {
+ today: 'Today',
+ yesterday: 'Yesterday',
+ thisWeek: 'This Week',
+ lastWeek: 'Last Week',
+ thisMonth: 'This Month',
+ lastMonth: 'Last Month',
+ thisYear: 'This Year',
+ lastYear: 'Last Year',
+ last7Days: 'Last 7 Days',
+ last30Days: 'Last 30 Days',
+ last90Days: 'Last 90 Days',
+ customDateRange: 'Custom Date Range',
+ },
};
diff --git a/src/i18n/tha.ts b/src/i18n/tha.ts
index 53c3aac2..3e590956 100644
--- a/src/i18n/tha.ts
+++ b/src/i18n/tha.ts
@@ -156,6 +156,9 @@ export default {
nativeLanguage: '{msg} ภาษาต้นทาง',
copy: 'คัดลอก',
paste: 'วาง',
+ period: 'ช่วงเวลา',
+ documentStatus: 'สถานะเอกสาร',
+ advanceSearch: 'ค้นหาขั้นสูง',
},
menu: {
@@ -1474,4 +1477,19 @@ export default {
type: 'ประเภท',
},
},
+
+ dateRange: {
+ today: 'วันนี้',
+ yesterday: 'เมื่อวานนี้',
+ thisWeek: 'สัปดาห์นี้',
+ lastWeek: 'สัปดาห์ที่แล้ว',
+ thisMonth: 'เดือนนี้',
+ lastMonth: 'เดือนที่แล้ว',
+ thisYear: 'ปีนี้',
+ lastYear: 'ปีที่แล้ว',
+ last7Days: '7 วันที่ผ่านมา',
+ last30Days: '30 วันที่ผ่านมา',
+ last90Days: '90 วันที่ผ่านมา',
+ customDateRange: 'กำหนดช่วงวันที่เอง',
+ },
};
From 4e86a90b0e9aa10124aa5f4cfffd6df19bbb544f Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 17 Apr 2025 13:53:36 +0700
Subject: [PATCH 063/331] feat: add AdvanceSearch component for date range
selection
---
src/components/shared/AdvanceSearch.vue | 188 ++++++++++++++++++++++++
1 file changed, 188 insertions(+)
create mode 100644 src/components/shared/AdvanceSearch.vue
diff --git a/src/components/shared/AdvanceSearch.vue b/src/components/shared/AdvanceSearch.vue
new file mode 100644
index 00000000..9557fc67
--- /dev/null
+++ b/src/components/shared/AdvanceSearch.vue
@@ -0,0 +1,188 @@
+
+
+
+
+
+
+ {{ $t('general.advanceSearch') }}
+
+ (date = [])"
+ />
+
+ (isDateSelect = true)"
+ @closed="() => (isDateSelect = false)"
+ >
+
+
+
+
+
+
+ {{
+ date
+ ? dateFormatJS({ date: date[0] }) +
+ ' - ' +
+ dateFormatJS({ date: date[1] })
+ : ''
+ }}
+
+
+
+
+
+
+
+
+
+
+ {{ $t('general.advanceSearch') }}
+
+
+
From e189b9a88045c92174fc9214dc6cfec6a8778670 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 17 Apr 2025 14:15:04 +0700
Subject: [PATCH 064/331] feat: conditionally render file input based on user
type
---
src/components/02_personnel-management/FormByType.vue | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/components/02_personnel-management/FormByType.vue b/src/components/02_personnel-management/FormByType.vue
index 114fd1b9..7f850999 100644
--- a/src/components/02_personnel-management/FormByType.vue
+++ b/src/components/02_personnel-management/FormByType.vue
@@ -261,6 +261,7 @@ function deleteFile(name: string) {
Date: Thu, 17 Apr 2025 14:25:46 +0700
Subject: [PATCH 065/331] feat: add contact name and contact tel fields to user
types and forms
---
.../02_personnel-management/FormPerson.vue | 317 +++++-------------
.../FormBasicInfoAgencies.vue | 2 +-
src/components/shared/SelectInput.vue | 3 +-
src/i18n/eng.ts | 20 +-
src/i18n/tha.ts | 2 +
.../02_personnel-management/MainPage.vue | 12 +
src/stores/user/types.ts | 6 +-
7 files changed, 116 insertions(+), 246 deletions(-)
diff --git a/src/components/02_personnel-management/FormPerson.vue b/src/components/02_personnel-management/FormPerson.vue
index 0204bb47..17debb1e 100644
--- a/src/components/02_personnel-management/FormPerson.vue
+++ b/src/components/02_personnel-management/FormPerson.vue
@@ -1,10 +1,8 @@
@@ -171,41 +96,17 @@ watch(
for="input-citizen-id"
/>
- (typeof v === 'string' ? (prefixName = v) : '')
- "
- @clear="prefixName = ''"
:rules="[(val: string) => !!val || $t('form.error.required')]"
- >
-
-
-
- {{ $t('general.noData') }}
-
-
-
-
+ :label="$t('personnel.form.prefixName')"
+ class="col-md-1 col-6"
+ v-model="prefixName"
+ />
- (typeof v === 'string' ? (prefixName = v) : '')
- "
- @clear="prefixName = ''"
:rules="[(val: string) => !!val || $t('form.error.required')]"
- >
-
-
-
- {{ $t('general.noData') }}
-
-
-
-
+ :label="$t('personnel.form.prefixName')"
+ class="col-md-1 col-6"
+ v-model="prefixName"
+ />
- (typeof v === 'string' ? (gender = v) : '')"
- @clear="gender = ''"
- >
-
-
-
- {{ $t('general.noData') }}
-
-
-
-
+ class="col-md-2 col-6"
+ v-model="gender"
+ />
-
-
-
-
- {{ $t('general.noData') }}
-
-
-
-
-
+
+
+ (typeof v === 'string' ? (contactName = v) : '')
+ "
+ />
+
+ (typeof v === 'string' ? (contactTel = v) : '')
+ "
>
-
-
-
- {{ $t('general.noData') }}
-
-
+
+
-
+
diff --git a/src/components/07_agencies-management/FormBasicInfoAgencies.vue b/src/components/07_agencies-management/FormBasicInfoAgencies.vue
index f01bed38..7f3f3c5d 100644
--- a/src/components/07_agencies-management/FormBasicInfoAgencies.vue
+++ b/src/components/07_agencies-management/FormBasicInfoAgencies.vue
@@ -133,7 +133,7 @@ type Options = { label: string; value: string };
:readonly="readonly"
hide-bottom-space
class="col-md-4 col-12"
- :label="$t('form.telephone')"
+ :label="$t('agencies.contactTel')"
:model-value="readonly ? contactTel || '-' : contactTel"
@update:model-value="
(v) => (typeof v === 'string' ? (contactTel = v) : '')
diff --git a/src/components/shared/SelectInput.vue b/src/components/shared/SelectInput.vue
index d3317e79..ff7403d4 100644
--- a/src/components/shared/SelectInput.vue
+++ b/src/components/shared/SelectInput.vue
@@ -28,6 +28,7 @@ const props = withDefaults(
disable?: boolean;
multiple?: boolean;
hideInput?: boolean;
+ hideDropdownIcon?: boolean;
rules?: ((value: string) => string | true)[];
}>(),
@@ -82,7 +83,7 @@ watch(
:hide-selected
hide-bottom-space
:fill-input="fillInput && !!model"
- :hide-dropdown-icon="readonly"
+ :hide-dropdown-icon="readonly || hideDropdownIcon"
input-debounce="500"
:option-value="
typeof props.optionValue === 'string' ? props.optionValue : 'value'
diff --git a/src/i18n/eng.ts b/src/i18n/eng.ts
index 00d10ae3..2cccdd38 100644
--- a/src/i18n/eng.ts
+++ b/src/i18n/eng.ts
@@ -60,7 +60,7 @@ export default {
branchStatus: 'Branch Status',
success: 'Success',
taxNo: 'Legal Person',
- contactName: 'Contact Name',
+ contactName: 'Contact Person',
image: 'Image of ',
apply: 'Apply',
licenseNumber: 'License number',
@@ -382,7 +382,7 @@ export default {
branchLabel: 'Branch',
branchHQLabel: 'Headoffice',
taxNo: 'Legal Person',
- contactName: 'Contact Name',
+ contactName: 'Contact Person',
},
page: {
captionManage: 'Manage',
@@ -403,8 +403,8 @@ export default {
code: 'Headoffice Code',
codeBranch: 'Branch Code',
taxNo: 'Tax Identification Number',
- contactName: 'Contact Name',
- contactTelephone: 'Contact Telephone',
+ contactName: 'Contact Person',
+ contactTelephone: 'Contact Number',
branchName: 'Branch Name',
branchNameEN: 'Branch Name (EN)',
servicePointName: 'Service Point Name',
@@ -469,6 +469,8 @@ export default {
normal: 'Normal',
canceled: 'Canceled',
blacklist: 'Black list',
+ contactName: 'Contact Person',
+ contactTel: 'Contact Number',
},
},
customer: {
@@ -560,7 +562,7 @@ export default {
jobPosition: 'Job Position',
address: 'Address',
workPlace: 'Workplace',
- contactName: 'Contact Name',
+ contactName: 'Contact Person',
contactPhone: 'Contact Phone',
totalEmployee: 'Total Employee',
officeTel: 'Headoffice Telephone',
@@ -798,7 +800,7 @@ export default {
employee: 'Employee',
employeeName: 'Full Name',
workName: 'Work Name',
- contactName: 'Contact Name',
+ contactName: 'Contact Person',
documentReceivePoint: 'Document Drop-Off Point"',
dueDate: 'Quotation Due Date',
specialCondition: 'Special Conditions',
@@ -916,8 +918,8 @@ export default {
code: 'Agencies Code',
group: 'Agencies Group',
name: 'Agencies Name',
- contactName: 'Contact Name',
- contactTel: 'Contact Telephone',
+ contactName: 'Contact Person',
+ contactTel: 'Contact Number',
bankInfo: 'Bank Information',
},
@@ -1006,7 +1008,7 @@ export default {
issueBranch: 'Issue Branch',
issueDate: 'Issue Date',
madeBy: 'Made By',
- contactName: 'Contact Name',
+ contactName: 'Contact Person',
workOrderCode: 'Work Order Code',
workOrderName: 'Work Order Name',
telephone: 'Telephone',
diff --git a/src/i18n/tha.ts b/src/i18n/tha.ts
index 3e590956..d903e79e 100644
--- a/src/i18n/tha.ts
+++ b/src/i18n/tha.ts
@@ -465,6 +465,8 @@ export default {
normal: 'ปกติ',
canceled: 'ยกเลิก',
blacklist: 'แบล็คลิสต์',
+ contactName: 'ชื่อผู้ติดต่อ',
+ contactTel: 'เบอร์โทรศัพท์ผู้ติดต่อ',
},
},
customer: {
diff --git a/src/pages/02_personnel-management/MainPage.vue b/src/pages/02_personnel-management/MainPage.vue
index f71b2ec1..b7c152d1 100644
--- a/src/pages/02_personnel-management/MainPage.vue
+++ b/src/pages/02_personnel-management/MainPage.vue
@@ -151,6 +151,8 @@ const defaultFormData = {
citizenExpire: null,
citizenIssue: null,
citizenId: '',
+ contactName: '',
+ contactTel: '',
remark: '',
agencyStatus: '',
};
@@ -201,6 +203,8 @@ const formData = ref({
citizenExpire: null,
citizenIssue: null,
citizenId: '',
+ contactName: '',
+ contactTel: '',
remark: '',
agencyStatus: '',
});
@@ -590,6 +594,8 @@ async function assignFormData(idEdit: string) {
responsibleArea: foundUser.responsibleArea,
status: foundUser.status,
selectedImage: foundUser.selectedImage,
+ contactName: foundUser.contactName,
+ contactTel: foundUser.contactTel,
licenseExpireDate:
(foundUser.licenseExpireDate &&
new Date(foundUser.licenseExpireDate)) ||
@@ -1752,12 +1758,15 @@ watch(
v-model:citizen-id="formData.citizenId"
v-model:citizen-issue="formData.citizenIssue"
v-model:citizen-expire="formData.citizenExpire"
+ v-model:contact-name="formData.contactName"
+ v-model:contact-tel="formData.contactTel"
:title="'personnel.form.personalInformation'"
prefix-id="drawer-info-personnel"
dense
outlined
separator
:readonly="!infoDrawerEdit"
+ :agency="formData.userType === 'AGENCY'"
class="q-mb-xl"
/>
@@ -1967,6 +1976,7 @@ watch(
id="dialog-form-personal"
prefix-id="form-dialog-personnel"
dense
+ :agency="formData.userType === 'AGENCY'"
outlined
separator
:title="'personnel.form.personalInformation'"
@@ -1985,6 +1995,8 @@ watch(
v-model:citizen-id="formData.citizenId"
v-model:citizen-issue="formData.citizenIssue"
v-model:citizen-expire="formData.citizenExpire"
+ v-model:contact-name="formData.contactName"
+ v-model:contact-tel="formData.contactTel"
class="q-mb-xl"
/>
Date: Thu, 17 Apr 2025 15:02:22 +0700
Subject: [PATCH 066/331] refactor: show all product and add column status and
edit format remark
---
src/i18n/eng.ts | 1 +
src/i18n/tha.ts | 1 +
src/pages/09_task-order/constants.ts | 6 +++
.../expansion/ProductExpansion.vue | 43 +++++++++++++++++--
.../09_task-order/order_view/MainPage.vue | 43 ++++++++++++++-----
src/stores/task-order/types.ts | 2 +
src/utils/string-template.ts | 12 +++++-
7 files changed, 92 insertions(+), 16 deletions(-)
diff --git a/src/i18n/eng.ts b/src/i18n/eng.ts
index 2cccdd38..fd4edc0e 100644
--- a/src/i18n/eng.ts
+++ b/src/i18n/eng.ts
@@ -159,6 +159,7 @@ export default {
period: 'Period',
documentStatus: 'Document Status',
advanceSearch: 'Advance Search',
+ totalPeople: '{meg} people',
},
menu: {
diff --git a/src/i18n/tha.ts b/src/i18n/tha.ts
index d903e79e..fe0045bb 100644
--- a/src/i18n/tha.ts
+++ b/src/i18n/tha.ts
@@ -159,6 +159,7 @@ export default {
period: 'ช่วงเวลา',
documentStatus: 'สถานะเอกสาร',
advanceSearch: 'ค้นหาขั้นสูง',
+ totalPeople: '{meg} คน',
},
menu: {
diff --git a/src/pages/09_task-order/constants.ts b/src/pages/09_task-order/constants.ts
index d497cef2..21257dad 100644
--- a/src/pages/09_task-order/constants.ts
+++ b/src/pages/09_task-order/constants.ts
@@ -227,6 +227,12 @@ export const productColumn = [
label: 'taskOrder.productList',
field: 'productList',
},
+ {
+ name: 'status',
+ align: 'center',
+ label: 'general.status',
+ field: 'status',
+ },
{
name: 'amountOfEmployee',
align: 'center',
diff --git a/src/pages/09_task-order/expansion/ProductExpansion.vue b/src/pages/09_task-order/expansion/ProductExpansion.vue
index f5f059f6..97a1f3bc 100644
--- a/src/pages/09_task-order/expansion/ProductExpansion.vue
+++ b/src/pages/09_task-order/expansion/ProductExpansion.vue
@@ -11,6 +11,8 @@ import { baseUrl, formatNumberDecimal, commaInput } from 'src/stores/utils';
import { precisionRound } from 'src/utils/arithmetic';
import { useConfigStore } from 'stores/config';
import { storeToRefs } from 'pinia';
+import BadgeComponent from 'src/components/BadgeComponent.vue';
+import { TaskStatus } from 'src/stores/task-order/types';
const currentBtnOpen = ref([]);
const configStore = useConfigStore();
@@ -30,7 +32,10 @@ const props = defineProps<{
readonly?: boolean;
agentPrice?: boolean;
taskList: {
- product: RequestWork['productService']['product'];
+ product: RequestWork['productService']['product'] & {
+ taskStatus?: TaskStatus;
+ totalNotStatusComplete?: number;
+ };
list: RequestWork[];
}[];
creditNote?: boolean;
@@ -111,6 +116,26 @@ function calcPrice(
return precisionRound(priceNoVat * amount + rawVatTotal);
}
+
+function taskOrderStatus(value: TaskStatus) {
+ if ([TaskStatus.Pending].includes(value)) {
+ return '--blue-6-hsl';
+ }
+ if ([TaskStatus.InProgress, TaskStatus.Validate].includes(value)) {
+ return '--orange-5-hsl';
+ }
+ if (
+ [
+ TaskStatus.Canceled,
+ TaskStatus.Restart,
+ TaskStatus.Redo,
+ TaskStatus.Failed,
+ ].includes(value)
+ ) {
+ return '--red-5-hsl';
+ }
+ return '--green-8-hsl';
+}
v.name !== 'discount' &&
v.name !== 'priceBeforeVat' &&
- v.name !== 'vat',
+ v.name !== 'vat' &&
+ v.name !== 'status',
)
: productColumn
"
@@ -173,7 +199,10 @@ function calcPrice(
{{ props.row.product.name }}
+
+
+
+
{{ props.row.list.length }}
diff --git a/src/pages/09_task-order/order_view/MainPage.vue b/src/pages/09_task-order/order_view/MainPage.vue
index bb69f6e6..f123ca74 100644
--- a/src/pages/09_task-order/order_view/MainPage.vue
+++ b/src/pages/09_task-order/order_view/MainPage.vue
@@ -280,7 +280,10 @@ let taskListGroup = computed(() => {
const cacheData = currentFormData.value.taskList.reduce<
{
- product: RequestWork['productService']['product'];
+ product: RequestWork['productService']['product'] & {
+ taskStatus?: TaskStatus;
+ totalNotStatusComplete?: number;
+ };
list: (RequestWork & {
_template?: {
id: string;
@@ -289,15 +292,15 @@ let taskListGroup = computed(() => {
step: number;
responsibleInstitution: (string | { group: string })[];
} | null;
+ taskStatus?: TaskStatus;
+ failedComment?: string;
+ failedType?: string;
})[];
}[]
>((acc, curr) => {
- if (
+ const isNotComplete =
fullTaskOrder.value?.taskOrderStatus === TaskOrderStatus.Complete &&
- curr.taskStatus !== TaskStatus.Complete
- ) {
- return acc;
- }
+ curr.taskStatus !== TaskStatus.Complete;
const task = curr.requestWorkStep;
const step = curr.step;
@@ -308,9 +311,18 @@ let taskListGroup = computed(() => {
let exist = acc.find(
(item) => task.requestWork.productService.productId == item.product.id,
);
- const record = Object.assign(task.requestWork, {
- _template: getTemplateData(task.requestWork, step),
- });
+
+ const record = Object.assign(
+ {
+ ...task.requestWork,
+ taskStatus: curr.taskStatus,
+ failedComment: curr.failedComment || '',
+ failedType: curr.failedType || '',
+ },
+ {
+ _template: getTemplateData(task.requestWork, step),
+ },
+ );
const template = getTemplateData(task.requestWork, step);
@@ -323,10 +335,18 @@ let taskListGroup = computed(() => {
}
if (exist) {
- exist.list.push(task.requestWork);
+ exist.list.push(record);
+ if (isNotComplete) {
+ exist.product.totalNotStatusComplete =
+ (exist.product.totalNotStatusComplete || undefined) + 1;
+ }
} else {
acc.push({
- product: task.requestWork.productService.product,
+ product: {
+ ...task.requestWork.productService.product,
+ taskStatus: curr.taskStatus || TaskStatus.Pending,
+ totalNotStatusComplete: isNotComplete ? 1 : undefined,
+ },
list: [record],
});
}
@@ -985,6 +1005,7 @@ watch(
"
/>
+ {{ console.log(taskListGroup) }}
-import { reactive, ref } from 'vue';
+import { reactive, ref, watch } from 'vue';
import { RequestData } from 'src/stores/request-list';
import { DialogContainer, DialogHeader } from 'src/components/dialog';
import {
@@ -8,12 +8,11 @@ import {
MainButton,
SaveButton,
} from 'src/components/button';
-import TableProductAndService from 'src/components/shared/table/TableProductAndService.vue';
-import { Product } from 'src/stores/product-service/types';
import FormResponsibleUser from './FormResponsibleUser.vue';
import FormGroupHead from './FormGroupHead.vue';
import TableRequestList from './TableRequestList.vue';
import { column } from './constants';
+import useAddressStore from 'src/stores/address';
defineProps<{
requestList: RequestData[];
@@ -37,6 +36,7 @@ enum Step {
const open = defineModel({ default: false });
const step = ref(Step.RequestList);
const selected = ref([]);
+const listSameArea = ref([]);
const form = reactive({
responsibleUserLocal: false,
responsibleUserId: '',
@@ -51,8 +51,18 @@ function reset() {
function prev() {
step.value = Step.RequestList;
- console.log(selected.value[0]);
}
+
+watch(
+ () => selected.value,
+ async () => {
+ if (selected.value.length === 1) {
+ const districtId = selected.value[0].quotation.customerBranch.districtId;
+ const ret = await useAddressStore().listSameOfficeArea(districtId);
+ if (ret) listSameArea.value = ret;
+ }
+ },
+);
@@ -89,6 +99,7 @@ function prev() {
hide-action
hide-view
checkable
+ :list-same-area="listSameArea"
:columns="column"
:rows="requestList"
:visible-columns="[...column.map((col) => col.name)]"
@@ -116,6 +127,7 @@ function prev() {
diff --git a/src/pages/08_request-list/TableRequestList.vue b/src/pages/08_request-list/TableRequestList.vue
index 0dc5b433..cbad4a7a 100644
--- a/src/pages/08_request-list/TableRequestList.vue
+++ b/src/pages/08_request-list/TableRequestList.vue
@@ -24,6 +24,7 @@ const props = withDefaults(
hideAction?: boolean;
hideView?: boolean;
checkable?: boolean;
+ listSameArea?: string[];
}>(),
{
row: () => [],
@@ -39,7 +40,7 @@ defineEmits<{
(e: 'rejectCancel', data: RequestData): void;
}>();
-const selected = defineModel('selected');
+const selected = defineModel('selected');
function responsiblePerson(quotation: QuotationFull): CreatedBy[] | undefined {
const productServiceList = quotation.productServiceList;
@@ -97,7 +98,7 @@ function getEmployeeName(
return (
{
['eng']: `${useOptionStore().mapOption(employee?.namePrefix || '')} ${employee?.firstNameEN} ${employee?.lastNameEN}`,
- ['tha']: `${useOptionStore().mapOption(employee?.namePrefix || '')} ${employee?.firstName} ${employee?.lastName}`,
+ ['tha']: `${useOptionStore().mapOption(employee?.namePrefix || '')} ${employee?.firstName || employee?.firstNameEN} ${employee?.lastName || employee?.lastNameEN}`,
}[opts?.locale || 'eng'] || '-'
);
}
@@ -119,6 +120,17 @@ function toEmployee(employee: RequestData['employee']) {
window.open(url.toString(), '_blank');
}
+
+function handleCheckAll() {
+ const filteredRows = props.rows.filter((row) =>
+ props.listSameArea?.includes(row.quotation.customerBranch.districtId),
+ );
+ if (selected.value.length === filteredRows.length) {
+ selected.value = [];
+ } else {
+ selected.value = filteredRows;
+ }
+}
-
+
+
{{ col.label && $t(col.label) }}
@@ -161,11 +184,29 @@ function toEmployee(employee: RequestData['employee']) {
} & Omit[0], 'row'>"
>
-
+
{{ props.rowIndex + 1 }}
@@ -487,4 +528,9 @@ function toEmployee(employee: RequestData['employee']) {
text-decoration: underline;
cursor: pointer;
}
+
+.disabled-row {
+ opacity: 0.3;
+ filter: grayscale(1);
+}
diff --git a/src/stores/address/index.ts b/src/stores/address/index.ts
index 975ca773..5cbbd14d 100644
--- a/src/stores/address/index.ts
+++ b/src/stores/address/index.ts
@@ -102,12 +102,25 @@ const useAddressStore = defineStore('api-address', () => {
return subDistrict.value[districtId];
}
+ async function listSameOfficeArea(districtId: string) {
+ const res = await api.post(
+ `/employment-office/list-same-office-area`,
+ { districtId: districtId },
+ );
+
+ if (!res) return false;
+
+ return res.data;
+ }
+
return {
fetchOffice,
fetchOfficeById,
fetchProvince,
fetchDistrictByProvinceId,
fetchSubDistrictByProvinceId,
+
+ listSameOfficeArea,
};
});
From d7e53b764cca1a746e2202433bb4b602d24fa0af Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 17 Apr 2025 16:15:08 +0700
Subject: [PATCH 068/331] feat: enhance AdvanceSearch component to support date
range selection and workflow template advance search
---
src/components/shared/AdvanceSearch.vue | 9 ++--
src/pages/04_flow-managment/MainPage.vue | 66 ++++++++++++++++--------
src/stores/workflow-template/index.ts | 2 +
3 files changed, 52 insertions(+), 25 deletions(-)
diff --git a/src/components/shared/AdvanceSearch.vue b/src/components/shared/AdvanceSearch.vue
index 9557fc67..5b5b33a9 100644
--- a/src/components/shared/AdvanceSearch.vue
+++ b/src/components/shared/AdvanceSearch.vue
@@ -9,7 +9,7 @@ defineProps<{
active?: boolean;
}>();
-const date = defineModel();
+const date = defineModel();
const dateRange = ref('');
const isDateSelect = ref(false);
@@ -64,13 +64,14 @@ function mapDateRange(val: string) {
end = today.endOf('day');
break;
case 'customDateRange':
- // Do nothing or allow manual date picking
- return;
+ start = today.startOf('day');
+ end = today.endOf('day');
+ break;
default:
return;
}
- return [start.toDate(), end.toDate()];
+ return [start.toDate().toISOString(), end.toDate().toISOString()];
}
watch(
diff --git a/src/pages/04_flow-managment/MainPage.vue b/src/pages/04_flow-managment/MainPage.vue
index 8cada9a1..ba335b77 100644
--- a/src/pages/04_flow-managment/MainPage.vue
+++ b/src/pages/04_flow-managment/MainPage.vue
@@ -1,6 +1,6 @@
pageState.inputSearch, workflowPageSize], () => {
-
-
-
-
+
+
+
+ {{ $t('general.status') }}
+
+
-
+
{
query?: string;
status?: Status;
activeOnly?: boolean;
+ startDate?: string;
+ endDate?: string;
}) {
const res = await api.get>(
'/workflow-template',
From 181ddc8f03fe3b98f8f5389be287ff717f795ae3 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 17 Apr 2025 16:15:33 +0700
Subject: [PATCH 069/331] feat: add dayjs library for date manipulation
---
package.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/package.json b/package.json
index be62c38a..9638bb29 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
"apexcharts": "^4.5.0",
"axios": "^1.8.4",
"cropperjs": "^1.6.2",
+ "dayjs": "^1.11.13",
"highlight.js": "^11.11.1",
"keycloak-js": "^25.0.6",
"markdown-it": "^14.1.0",
From fd5d4b797999a8b83f281a79cd92a5123f4f9420 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 17 Apr 2025 16:16:22 +0700
Subject: [PATCH 070/331] feat: add dayjs library version 1.11.13 to
pnpm-lock.yaml
---
pnpm-lock.yaml | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 47c2eb49..7cb78cc6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -29,6 +29,9 @@ importers:
cropperjs:
specifier: ^1.6.2
version: 1.6.2
+ dayjs:
+ specifier: ^1.11.13
+ version: 1.11.13
highlight.js:
specifier: ^11.11.1
version: 11.11.1
@@ -1473,6 +1476,9 @@ packages:
date-fns@3.6.0:
resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
+ dayjs@1.11.13:
+ resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
+
de-indent@1.0.2:
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
@@ -5272,6 +5278,8 @@ snapshots:
date-fns@3.6.0: {}
+ dayjs@1.11.13: {}
+
de-indent@1.0.2: {}
debug@2.6.9:
From efeb1b51eb4f9d762ea760e73902c5c8be36cd58 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 17 Apr 2025 16:23:52 +0700
Subject: [PATCH 071/331] feat: integrate date range selection in property
management and workflow lists
---
src/pages/04_flow-managment/MainPage.vue | 8 +--
src/pages/04_property-managment/MainPage.vue | 70 +++++++++++++-------
src/stores/property/index.ts | 2 +
3 files changed, 53 insertions(+), 27 deletions(-)
diff --git a/src/pages/04_flow-managment/MainPage.vue b/src/pages/04_flow-managment/MainPage.vue
index ba335b77..1285a99c 100644
--- a/src/pages/04_flow-managment/MainPage.vue
+++ b/src/pages/04_flow-managment/MainPage.vue
@@ -283,8 +283,8 @@ async function fetchWorkflowList(mobileFetch?: boolean) {
: statusFilter.value === 'statusACTIVE'
? 'ACTIVE'
: 'INACTIVE',
- startDate: pageState.searchDate[0] || undefined,
- endDate: pageState.searchDate[1] || undefined,
+ startDate: pageState.searchDate[0],
+ endDate: pageState.searchDate[1],
});
if (res) {
workflowData.value =
@@ -533,12 +533,12 @@ watch(
class="col surface-2 flex items-center justify-center"
>
({
type: {},
});
const statusFilter = ref<'all' | 'statusACTIVE' | 'statusINACTIVE'>('all');
-const refFilter = ref>();
const fieldSelected = ref<('order' | 'name' | 'type')[]>([
'order',
'name',
@@ -118,6 +118,7 @@ const pageState = reactive({
addModal: false,
viewDrawer: false,
isDrawerEdit: true,
+ searchDate: [],
});
async function fetchPropertyList(mobileFetch?: boolean) {
@@ -134,6 +135,8 @@ async function fetchPropertyList(mobileFetch?: boolean) {
: statusFilter.value === 'statusACTIVE'
? 'ACTIVE'
: 'INACTIVE',
+ startDate: pageState.searchDate[0],
+ endDate: pageState.searchDate[1],
});
if (res) {
propertyData.value =
@@ -317,11 +320,14 @@ watch(
fetchPropertyList();
},
);
-watch([() => pageState.inputSearch, propertyPageSize], () => {
- propertyData.value = [];
- propertyPage.value = 1;
- fetchPropertyList();
-});
+watch(
+ [() => pageState.inputSearch, propertyPageSize, () => pageState.searchDate],
+ () => {
+ propertyData.value = [];
+ propertyPage.value = 1;
+ fetchPropertyList();
+ },
+);
pageState.inputSearch, propertyPageSize], () => {
-
-
-
-
+
+
+
+ {{ $t('general.status') }}
+
+
-
+
pageState.inputSearch, propertyPageSize], () => {
class="col surface-2 flex items-center justify-center"
>
{
query?: string;
status?: Status;
activeOnly?: boolean;
+ startDate?: string;
+ endDate?: string;
}) {
const res = await api.get>('/property', {
params,
From 461dd359b19165221c0568f5e18c4441901f9aa4 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 17 Apr 2025 16:40:43 +0700
Subject: [PATCH 072/331] feat: add date range filtering to product and service
lists
---
src/pages/04_product-service/MainPage.vue | 145 +++++++++++++++-------
src/stores/product-service/index.ts | 6 +
2 files changed, 108 insertions(+), 43 deletions(-)
diff --git a/src/pages/04_product-service/MainPage.vue b/src/pages/04_product-service/MainPage.vue
index 687e2883..a7fdbc1e 100644
--- a/src/pages/04_product-service/MainPage.vue
+++ b/src/pages/04_product-service/MainPage.vue
@@ -3,7 +3,7 @@ import { nextTick, ref, watch, reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import { onMounted } from 'vue';
import { storeToRefs } from 'pinia';
-import { QSelect, useQuasar, type QTableProps } from 'quasar';
+import { useQuasar, type QTableProps } from 'quasar';
import DialogProperties from 'src/components/dialog/DialogProperties.vue';
import ProductCardComponent from 'components/04_product-service/ProductCardComponent.vue';
@@ -69,6 +69,7 @@ import {
import { useWorkflowTemplate } from 'src/stores/workflow-template';
import { deepEquals } from 'src/utils/arr';
import { toRaw } from 'vue';
+import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
const flowStore = useFlowStore();
const navigatorStore = useNavigator();
@@ -167,8 +168,6 @@ const splitterModel = computed(() =>
$q.screen.lt.md ? (productMode.value !== 'group' ? 0 : 100) : 25,
);
-const refFilterGroup = ref>();
-const refFilterProductService = ref>();
const holdDialog = ref(false);
const imageDialog = ref(false);
const currentNode = ref();
@@ -526,6 +525,7 @@ const currentStatusGroupType = ref('CREATED');
const currentIdGroupType = ref('');
const currentStatus = ref('All');
+const searchDate = ref([]);
// img
const isImageEdit = ref(false);
@@ -621,6 +621,8 @@ async function fetchListGroups(mobileFetch?: boolean) {
: currentStatus.value === 'ACTIVE'
? 'ACTIVE'
: 'INACTIVE',
+ startDate: searchDate.value[0],
+ endDate: searchDate.value[1],
});
if (res) {
@@ -681,6 +683,8 @@ async function fetchListOfProduct(mobileFetch?: boolean) {
? 'ACTIVE'
: undefined,
productGroupId: currentIdGroup.value,
+ startDate: searchDate.value[0],
+ endDate: searchDate.value[1],
});
if (res) {
@@ -726,6 +730,8 @@ async function fetchListOfService(mobileFetch?: boolean) {
? 'ACTIVE'
: undefined,
productGroupId: currentIdGroup.value,
+ startDate: searchDate.value[0],
+ endDate: searchDate.value[1],
});
if (res) {
@@ -1596,6 +1602,7 @@ async function enterNext(type: 'service' | 'product') {
inputSearchProductAndService.value = '';
currentStatus.value = 'All';
filterStat.value = [];
+ searchDate.value = [];
if (
expandedTree.value.length > 1 &&
@@ -1751,7 +1758,7 @@ watch(currentStatus, async () => {
flowStore.rotate();
});
-watch(inputSearch, async () => {
+watch([inputSearch, () => searchDate.value], async () => {
if (productMode.value === 'group') {
productGroup.value = [];
currentPageGroup.value = 1;
@@ -1760,7 +1767,7 @@ watch(inputSearch, async () => {
}
});
-watch(inputSearchProductAndService, async () => {
+watch([inputSearchProductAndService, () => searchDate.value], async () => {
product.value = [];
service.value = [];
currentPageServiceAndProduct.value = 1;
@@ -2015,19 +2022,34 @@ watch(
-
-
-
-
+
+
+
+ {{ $t('general.status') }}
+
+
-
+
@@ -2182,26 +2204,44 @@ watch(
-
-
-
-
+
+
+
+ {{ $t('general.status') }}
+
+
-
+
-
-
-
-
+
+
+
+ {{ $t('general.status') }}
+
+
-
+
{
query?: string;
status?: 'CREATED' | 'ACTIVE' | 'INACTIVE';
activeOnly?: boolean;
+ startDate?: string;
+ endDate?: string;
}) {
const params = new URLSearchParams();
@@ -142,6 +144,8 @@ const useProductServiceStore = defineStore('api-product-service', () => {
orderField?: string;
activeOnly?: boolean;
orderBy?: 'asc' | 'desc';
+ startDate?: string;
+ endDate?: string;
}) {
const params = new URLSearchParams();
@@ -249,6 +253,8 @@ const useProductServiceStore = defineStore('api-product-service', () => {
productGroupId?: string;
status?: string;
fullDetail?: boolean;
+ startDate?: string;
+ endDate?: string;
}) {
const params = new URLSearchParams();
From 36cef7ceb6f3168274807d8dadadd2cb3bcf5944 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 17 Apr 2025 17:03:16 +0700
Subject: [PATCH 073/331] feat: add date range selection to customer and
employee management
---
src/pages/03_customer-management/MainPage.vue | 90 ++++++++++++-------
src/stores/customer/index.ts | 2 +
src/stores/employee/index.ts | 2 +
3 files changed, 64 insertions(+), 30 deletions(-)
diff --git a/src/pages/03_customer-management/MainPage.vue b/src/pages/03_customer-management/MainPage.vue
index cee0de34..f78f1cb0 100644
--- a/src/pages/03_customer-management/MainPage.vue
+++ b/src/pages/03_customer-management/MainPage.vue
@@ -1,7 +1,7 @@
pageState.inputSearch, () => pageState.statusFilter], () => {
-
-
-
-
+
+
+
+ {{ $t('general.status') }}
+
+
-
+
{
responsibleOnly?: boolean;
quotationId?: string;
incomplete?: boolean;
+ startDate?: string;
+ endDate?: string;
}) {
const res = await api.get>('/request-data', {
params,
From 1e6be274e2163fb43773ccc95911cfd4f1ea89ba Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 17 Apr 2025 17:30:06 +0700
Subject: [PATCH 077/331] feat: add date range selection to task order
filtering
---
src/pages/09_task-order/MainPage.vue | 11 +++++++++++
src/stores/task-order/index.ts | 4 ++++
2 files changed, 15 insertions(+)
diff --git a/src/pages/09_task-order/MainPage.vue b/src/pages/09_task-order/MainPage.vue
index a842b431..cda7d75e 100644
--- a/src/pages/09_task-order/MainPage.vue
+++ b/src/pages/09_task-order/MainPage.vue
@@ -27,6 +27,7 @@ import useFlowStore from 'src/stores/flow';
import { pageTabs, column, pageTabsReceive } from './constants';
import { dialogWarningClose, isRoleInclude } from 'src/stores/utils';
import { PaginationResult } from 'src/types';
+import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
const { t } = useI18n();
const $q = useQuasar();
@@ -48,6 +49,7 @@ const pageState = reactive({
isMessenger: isRoleInclude(['messenger']),
receiveDialog: false,
isReceiveScan: false,
+ searchDate: [],
});
const taskOrderList = ref([]);
@@ -69,6 +71,8 @@ async function fetchTaskOrderList(opts?: { page?: number; pageSize?: number }) {
pageSize: opts?.pageSize || pageSize.value,
query: pageState.inputSearch === '' ? undefined : pageState.inputSearch,
userTaskStatus: pageState.currentTab as UserTaskStatus,
+ startDate: pageState.searchDate[0],
+ endDate: pageState.searchDate[1],
});
} else {
res = await taskOrderStore.getTaskOrderList({
@@ -76,6 +80,8 @@ async function fetchTaskOrderList(opts?: { page?: number; pageSize?: number }) {
pageSize: opts?.pageSize || pageSize.value,
query: pageState.inputSearch === '' ? undefined : pageState.inputSearch,
taskOrderStatus: pageState.currentTab as TaskOrderStatus | undefined,
+ startDate: pageState.searchDate[0],
+ endDate: pageState.searchDate[1],
});
}
if (res) {
@@ -157,6 +163,7 @@ watch(
() => pageState.inputSearch,
() => pageSize.value,
() => pageState.statusFilter,
+ () => pageState.searchDate,
],
() => {
fetchTaskOrderList();
@@ -299,6 +306,10 @@ watch(
+
+
+
+
diff --git a/src/stores/task-order/index.ts b/src/stores/task-order/index.ts
index acc7f7a1..abb6c553 100644
--- a/src/stores/task-order/index.ts
+++ b/src/stores/task-order/index.ts
@@ -48,6 +48,8 @@ export const useTaskOrderStore = defineStore('taskorder-store', () => {
query?: string;
taskOrderStatus?: TaskOrderStatus;
assignedUserId?: boolean;
+ startDate?: string;
+ endDate?: string;
}) {
const res = await api.get >('/task-order', {
params,
@@ -161,6 +163,8 @@ export const useTaskOrderStore = defineStore('taskorder-store', () => {
pageSize?: number;
query?: string;
userTaskStatus?: UserTaskStatus;
+ startDate?: string;
+ endDate?: string;
}) {
const res = await api.get>('/user-task-order', {
params,
From ac42ee60d8f3e99fa4e0bc971c78c7160a403eff Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 17 Apr 2025 17:38:09 +0700
Subject: [PATCH 078/331] feat: add date range selection to credit note, debit
note, and receipt management
---
src/pages/11_credit-note/MainPage.vue | 9 +++++
src/pages/12_debit-note/MainPage.vue | 9 +++++
src/pages/13_receipt/MainPage.vue | 52 +++++++++++++++++++--------
src/stores/credit-note/index.ts | 2 ++
src/stores/debit-note/index.ts | 2 ++
src/stores/payment/index.ts | 2 ++
6 files changed, 61 insertions(+), 15 deletions(-)
diff --git a/src/pages/11_credit-note/MainPage.vue b/src/pages/11_credit-note/MainPage.vue
index 327c942b..1cea36fe 100644
--- a/src/pages/11_credit-note/MainPage.vue
+++ b/src/pages/11_credit-note/MainPage.vue
@@ -24,6 +24,7 @@ import { pageTabs, columns, hslaColors } from './constants';
import { CreditNoteStatus, useCreditNote } from 'src/stores/credit-note';
import TableCreditNote from './TableCreditNote.vue';
import { dialogWarningClose } from 'src/stores/utils';
+import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
const $q = useQuasar();
const { t } = useI18n();
@@ -46,6 +47,7 @@ const pageState = reactive({
total: 0,
creditDialog: false,
+ searchDate: [],
});
const fieldSelectedOption = computed(() => {
@@ -64,6 +66,8 @@ async function getList(opts?: { page?: number; pageSize?: number }) {
pageSize: opts?.pageSize || pageSize.value,
query: pageState.inputSearch === '' ? undefined : pageState.inputSearch,
creditNoteStatus: pageState.currentTab as CreditNoteStatus | undefined,
+ startDate: pageState.searchDate[0],
+ endDate: pageState.searchDate[1],
});
if (res) {
@@ -133,6 +137,7 @@ watch(
() => pageState.inputSearch,
() => pageSize.value,
() => pageState.statusFilter,
+ () => pageState.searchDate,
],
() => {
getList();
@@ -228,6 +233,10 @@ watch(
+
+
+
+
diff --git a/src/pages/12_debit-note/MainPage.vue b/src/pages/12_debit-note/MainPage.vue
index cd022dfa..15e2fc32 100644
--- a/src/pages/12_debit-note/MainPage.vue
+++ b/src/pages/12_debit-note/MainPage.vue
@@ -24,6 +24,7 @@ import { pageTabs, columns, hslaColors } from './constants';
import { DebitNoteStatus, useDebitNote } from 'src/stores/debit-note';
import { dialogWarningClose } from 'src/stores/utils';
import { useQuasar } from 'quasar';
+import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
const $q = useQuasar();
const { t } = useI18n();
@@ -46,6 +47,7 @@ const pageState = reactive({
total: 0,
debitDialog: false,
+ searchDate: [],
});
const fieldSelectedOption = computed(() => {
@@ -68,6 +70,8 @@ async function getList(opts?: { page?: number; pageSize?: number }) {
? undefined
: pageState.currentTab) as DebitNoteStatus,
includeRegisteredBranch: true,
+ startDate: pageState.searchDate[0],
+ endDate: pageState.searchDate[1],
});
if (res) {
@@ -149,6 +153,7 @@ watch(
() => pageState.inputSearch,
() => pageSize.value,
() => pageState.statusFilter,
+ () => pageState.searchDate,
],
() => getList(),
);
@@ -256,6 +261,10 @@ watch(
+
+
+
+
diff --git a/src/pages/13_receipt/MainPage.vue b/src/pages/13_receipt/MainPage.vue
index 8dede01c..3e6083ce 100644
--- a/src/pages/13_receipt/MainPage.vue
+++ b/src/pages/13_receipt/MainPage.vue
@@ -17,7 +17,8 @@ import { columns, hslaColors } from './constants';
import useFlowStore from 'src/stores/flow';
import { usePayment, useReceipt } from 'src/stores/payment';
import { PaymentDataStatus } from 'src/stores/payment/types';
-import { QSelect, useQuasar } from 'quasar';
+import { useQuasar } from 'quasar';
+import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
const $q = useQuasar();
const navigatorStore = useNavigator();
@@ -26,7 +27,6 @@ const receiptStore = useReceipt();
const { data, page, pageMax, pageSize } = storeToRefs(receiptStore);
// NOTE: Variable
-const refFilter = ref >();
const pageState = reactive({
hideStat: false,
@@ -35,6 +35,7 @@ const pageState = reactive({
fieldSelected: [...columns.map((v) => v.name)],
gridView: false,
total: 0,
+ searchDate: [],
});
const fieldSelectedOption = computed(() => {
@@ -49,6 +50,8 @@ async function fetchList(opts?: { rotateFlowId?: boolean }) {
page: page.value,
pageSize: pageSize.value,
query: pageState.inputSearch,
+ startDate: pageState.searchDate[0],
+ endDate: pageState.searchDate[1],
});
if (ret) {
data.value = $q.screen.xs ? [...data.value, ...ret.result] : ret.result;
@@ -95,6 +98,7 @@ watch(
() => pageState.inputSearch,
() => pageState.statusFilter,
() => pageSize.value,
+ () => pageState.searchDate,
],
() => {
page.value = 1;
@@ -172,25 +176,43 @@ watch(
-
-
-
-
+
+
+
+ {{ $t('general.status') }}
+
+
-
+
>(`/${ENDPOINT}`, {
params,
diff --git a/src/stores/debit-note/index.ts b/src/stores/debit-note/index.ts
index e9a71818..6d8638f8 100644
--- a/src/stores/debit-note/index.ts
+++ b/src/stores/debit-note/index.ts
@@ -28,6 +28,8 @@ export async function getDebitNoteList(params?: {
query?: string;
status?: Status;
includeRegisteredBranch?: boolean;
+ startDate?: string;
+ endDate?: string;
}) {
const res = await api.get>(`/${ENDPOINT}`, {
params,
diff --git a/src/stores/payment/index.ts b/src/stores/payment/index.ts
index e1258bdc..960c8a23 100644
--- a/src/stores/payment/index.ts
+++ b/src/stores/payment/index.ts
@@ -101,6 +101,8 @@ export const useReceipt = defineStore('receipt-store', () => {
debitNoteId?: string;
debitNoteOnly?: boolean;
quotationOnly?: boolean;
+ startDate?: string;
+ endDate?: string;
}) {
const res = await api.get>('/receipt', {
params: opts,
From 648ed3818179ccfff3178184b09db312dd35cda9 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 17 Apr 2025 17:45:26 +0700
Subject: [PATCH 079/331] refactor: comment out condition for resetting search
date on tab change
---
src/pages/03_customer-management/MainPage.vue | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/pages/03_customer-management/MainPage.vue b/src/pages/03_customer-management/MainPage.vue
index f78f1cb0..cfd29786 100644
--- a/src/pages/03_customer-management/MainPage.vue
+++ b/src/pages/03_customer-management/MainPage.vue
@@ -236,7 +236,7 @@ watch(
searchDate,
],
async ([tabName], [oldTabName]) => {
- if (tabName !== oldTabName) searchDate.value = [];
+ // if (tabName !== oldTabName) searchDate.value = [];
if (tabName === 'employer') {
currentPageCustomer.value = 1;
currentBtnOpen.value = [];
From 285b821c166bd5c5abd9d985c712ceceffe651ff Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 17 Apr 2025 18:04:12 +0700
Subject: [PATCH 080/331] feat: add date range search functionality to
personnel management
---
.../02_personnel-management/MainPage.vue | 57 +++++++++++++------
src/stores/user/index.ts | 2 +
2 files changed, 42 insertions(+), 17 deletions(-)
diff --git a/src/pages/02_personnel-management/MainPage.vue b/src/pages/02_personnel-management/MainPage.vue
index b7c152d1..b3ea5058 100644
--- a/src/pages/02_personnel-management/MainPage.vue
+++ b/src/pages/02_personnel-management/MainPage.vue
@@ -10,7 +10,7 @@ import useOptionStore from 'stores/options';
import useAddressStore from 'stores/address';
import useMyBranch from 'src/stores/my-branch';
import { calculateAge } from 'src/utils/datetime';
-import { QSelect, useQuasar, type QTableProps } from 'quasar';
+import { useQuasar, type QTableProps } from 'quasar';
import { dialog, baseUrl, setPrefixName } from 'stores/utils';
import { useNavigator } from 'src/stores/navigator';
import { isRoleInclude, resetScrollBar } from 'src/stores/utils';
@@ -49,6 +49,7 @@ import FormPerson from 'components/02_personnel-management/FormPerson.vue';
import FormByType from 'components/02_personnel-management/FormByType.vue';
import FormInformation from 'components/02_personnel-management/FormInformation.vue';
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
+import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
const { locale, t } = useI18n();
const $q = useQuasar();
@@ -73,7 +74,6 @@ const isImageEdit = ref(false);
const imageDialog = ref(false);
const infoDrawerEdit = ref(false);
const refreshImageState = ref(false);
-const refFilter = ref>();
const firstScroll = ref(false);
const inputSearch = ref('');
@@ -99,6 +99,8 @@ const userFileList = ref<{ name: string; url: string }[]>([]);
const typeStats = ref();
const userStats = ref();
+const searchDate = ref([]);
+
const urlProfile = ref();
const profileFileImg = ref(null);
const imageList = ref<{ selectedImage: string; list: string[] }>();
@@ -672,6 +674,8 @@ async function fetchUserList(mobileFetch?: boolean) {
: statusFilter.value === 'statusACTIVE'
? 'ACTIVE'
: 'INACTIVE',
+ startDate: searchDate.value[0],
+ endDate: searchDate.value[1],
});
if (ret) {
@@ -761,7 +765,7 @@ watch(
},
);
-watch([inputSearch, statusFilter, pageSize], async () => {
+watch([inputSearch, statusFilter, pageSize, searchDate], async () => {
if (userData.value) userData.value.result = [];
currentPage.value = 1;
@@ -883,26 +887,45 @@ watch(
-
-
-
-
+
+
+
+ {{ $t('general.status') }}
+
+
-
+
{
status?: Status;
responsibleDistrictId?: string;
activeBranchOnly?: boolean;
+ startDate?: string;
+ endDate?: string;
}) {
const res = await api.get>('/user', { params: opts });
From 03b03b4bc8278cbc619be957d382123e6737959c Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Fri, 18 Apr 2025 09:24:41 +0700
Subject: [PATCH 081/331] feat: implement date range filtering in branch
management
---
src/pages/01_branch-management/MainPage.vue | 69 +++++++++++++++------
src/stores/branch/index.ts | 2 +
2 files changed, 53 insertions(+), 18 deletions(-)
diff --git a/src/pages/01_branch-management/MainPage.vue b/src/pages/01_branch-management/MainPage.vue
index ef4acc82..83ddc3c6 100644
--- a/src/pages/01_branch-management/MainPage.vue
+++ b/src/pages/01_branch-management/MainPage.vue
@@ -6,7 +6,7 @@ import { Icon } from '@iconify/vue';
import { BranchContact } from 'stores/branch-contact/types';
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
-import type { QSelect, QTableProps, QTableSlots } from 'quasar';
+import type { QTableProps, QTableSlots } from 'quasar';
import { resetScrollBar } from 'src/stores/utils';
import useBranchStore from 'stores/branch';
import useFlowStore from 'stores/flow';
@@ -52,6 +52,7 @@ import {
UndoButton,
} from 'components/button';
import { useNavigator } from 'src/stores/navigator';
+import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
const $q = useQuasar();
const { t } = useI18n();
@@ -72,7 +73,6 @@ const typeBranchItem = [
color: 'var(--blue-6-hsl)',
},
];
-const refFilter = ref>();
const holdDialog = ref(false);
const isSubCreate = ref(false);
const columns = [
@@ -175,6 +175,8 @@ const qrCodeDialog = ref(false);
const qrCodeimageUrl = ref('');
const formLastSubBranch = ref(0);
+const searchDate = ref([]);
+
const branchStore = useBranchStore();
const flowStore = useFlowStore();
const { locale } = useI18n();
@@ -715,12 +717,20 @@ async function fetchList(opts: {
tree?: boolean;
withHead?: boolean;
filter?: 'head' | 'sub';
+ startDate?: string;
+ endDate?: string;
}) {
await branchStore.fetchList(opts);
}
-watch(inputSearch, () => {
- fetchList({ tree: true, query: inputSearch.value, withHead: true });
+watch([inputSearch, searchDate], () => {
+ fetchList({
+ tree: true,
+ query: inputSearch.value,
+ withHead: true,
+ startDate: searchDate.value[0],
+ endDate: searchDate.value[1],
+ });
currentSubBranch.value = undefined;
});
@@ -1170,26 +1180,49 @@ watch(currentHq, () => {
-
-
-
-
+
+
+
+
+ {{ $t('general.status') }}
+
+
-
+
{
withHead?: boolean;
activeOnly?: boolean;
headOfficeId?: string;
+ startDate?: string;
+ endDate?: string;
},
Data extends Pagination,
>(opts?: Options): Promise {
From 88f40dcb477ce3fe9479c2c8318cb13022ab4893 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Fri, 18 Apr 2025 09:29:46 +0700
Subject: [PATCH 082/331] feat: update agencies management to include date
range selection and refactor image list handling
---
.../07_agencies-management/AgenciesDialog.vue | 8 +--
src/pages/07_agencies-management/MainPage.vue | 61 +++++++++++++------
src/stores/institution/index.ts | 2 +
3 files changed, 47 insertions(+), 24 deletions(-)
diff --git a/src/pages/07_agencies-management/AgenciesDialog.vue b/src/pages/07_agencies-management/AgenciesDialog.vue
index 91b93c31..c71cb3e9 100644
--- a/src/pages/07_agencies-management/AgenciesDialog.vue
+++ b/src/pages/07_agencies-management/AgenciesDialog.vue
@@ -29,10 +29,10 @@ const drawerModel = defineModel('drawerModel', {
required: true,
default: false,
});
-const onCreateImageList = defineModel<{
+const imageListOnCreate = defineModel<{
selectedImage: string;
list: { url: string; imgFile: File | null; name: string }[];
-}>('onCreateImageList', { default: { selectedImage: '', list: [] } });
+}>('imageListOnCreate', { default: { selectedImage: '', list: [] } });
const imageState = reactive({
imageDialog: false,
@@ -159,7 +159,7 @@ async function submitImage(name: string) {
function clearImageState() {
imageState.imageDialog = false;
imageFile.value = null;
- onCreateImageList.value = { selectedImage: '', list: [] };
+ imageListOnCreate.value = { selectedImage: '', list: [] };
imageState.refreshImageState = false;
}
@@ -531,7 +531,7 @@ watch(
-import { QSelect, QTableProps } from 'quasar';
+import { QTableProps } from 'quasar';
import { dialog } from 'src/stores/utils';
import { onMounted, reactive, ref, watch } from 'vue';
import { storeToRefs } from 'pinia';
@@ -21,6 +21,7 @@ import FloatingActionButton from 'src/components/FloatingActionButton.vue';
import CreateButton from 'src/components/AddButton.vue';
import NoData from 'src/components/NoData.vue';
import AgenciesDialog from './AgenciesDialog.vue';
+import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
const { t } = useI18n();
const $q = useQuasar();
@@ -78,6 +79,7 @@ const pageState = reactive({
addModal: false,
viewDrawer: false,
isDrawerEdit: true,
+ searchDate: [],
});
const blankFormData: InstitutionPayload = {
@@ -114,11 +116,10 @@ const blankFormData: InstitutionPayload = {
};
const statusFilter = ref<'all' | 'statusACTIVE' | 'statusINACTIVE'>('all');
-const refFilter = ref>();
const refAgenciesDialog = ref();
const formData = ref(structuredClone(blankFormData));
const currAgenciesData = ref();
-const onCreateImageList = ref<{
+const imageListOnCreate = ref<{
selectedImage: string;
list: { url: string; imgFile: File | null; name: string }[];
}>({ selectedImage: '', list: [] });
@@ -248,7 +249,7 @@ async function submit(opt?: { selectedImage: string }) {
...payload,
code: formData.value.group || '',
},
- onCreateImageList.value,
+ imageListOnCreate.value,
);
await fetchData($q.screen.xs);
@@ -288,6 +289,8 @@ async function fetchData(mobileFetch?: boolean) {
: statusFilter.value === 'statusACTIVE'
? 'ACTIVE'
: 'INACTIVE',
+ startDate: pageState.searchDate[0],
+ endDate: pageState.searchDate[1],
});
if (ret) {
@@ -357,7 +360,7 @@ onMounted(async () => {
});
watch(
- () => [pageState.inputSearch, statusFilter.value],
+ () => [pageState.inputSearch, statusFilter.value, pageState.searchDate],
() => {
page.value = 1;
data.value = [];
@@ -440,26 +443,44 @@ watch(
-
-
-
-
+
+
+
+ {{ $t('general.status') }}
+
+
-
+
diff --git a/src/pages/04_flow-managment/FlowDialog.vue b/src/pages/04_flow-managment/FlowDialog.vue
index 77d8bd12..e3958ac8 100644
--- a/src/pages/04_flow-managment/FlowDialog.vue
+++ b/src/pages/04_flow-managment/FlowDialog.vue
@@ -60,6 +60,7 @@ async function addStep() {
flowData.value.step.push({
responsibleInstitution: [],
responsiblePersonId: [],
+ responsibleGroup: [],
value: [],
detail: '',
name: '',
@@ -166,6 +167,7 @@ function triggerPropertiesDialog(step: WorkFlowPayloadStep) {
id="flow-form-dialog"
>
{
- userInTable.value[i] = { name: s.name, responsiblePerson: [] };
+ userInTable.value[i] = {
+ name: s.name,
+ responsiblePerson: [],
+ responsibleGroup: [],
+ };
s.responsiblePerson.forEach((p) => {
userInTable.value[i].responsiblePerson.push({
id: p.user.id,
@@ -237,12 +242,16 @@ function assignFormData(workflowData: WorkflowTemplate) {
code: p.user.code,
});
});
+ s.responsibleGroup.forEach((g) => {
+ userInTable.value[i].responsibleGroup.push(g);
+ });
return {
id: s.id,
name: s.name,
detail: s.detail,
messengerByArea: s.messengerByArea || false,
value: s.value.length > 0 ? JSON.parse(JSON.stringify(s.value)) : [],
+ responsibleGroup: s.responsibleGroup.map((g) => g),
responsiblePersonId: s.responsiblePerson.map((p) => p.userId),
responsibleInstitution: JSON.parse(
JSON.stringify(s.responsibleInstitution),
diff --git a/src/stores/workflow-template/index.ts b/src/stores/workflow-template/index.ts
index 9f186a17..ef1e83d5 100644
--- a/src/stores/workflow-template/index.ts
+++ b/src/stores/workflow-template/index.ts
@@ -2,7 +2,7 @@ import { ref } from 'vue';
import { defineStore } from 'pinia';
import { api } from 'src/boot/axios';
import { PaginationResult } from 'src/types';
-import { WorkflowTemplate, WorkflowTemplatePayload, Group } from './types';
+import { Group, WorkflowTemplate, WorkflowTemplatePayload } from './types';
import { Status } from '../types';
export const useWorkflowTemplate = defineStore('workflow-store', () => {
diff --git a/src/stores/workflow-template/types.ts b/src/stores/workflow-template/types.ts
index 7307c2cf..c2c64036 100644
--- a/src/stores/workflow-template/types.ts
+++ b/src/stores/workflow-template/types.ts
@@ -50,7 +50,7 @@ export type WorkflowTemplatePayload = {
export type WorkflowUserInTable = {
name: string;
- responsibleGroup: Group[];
+ responsibleGroup: string[];
responsiblePerson: {
id: string;
selectedImage?: string;
From 92b4db45d2e8006ca9ebbde72fd98470780fecfe Mon Sep 17 00:00:00 2001
From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com>
Date: Thu, 24 Apr 2025 14:32:10 +0700
Subject: [PATCH 094/331] feat: detect can edit request list
---
src/pages/08_request-list/RequestListView.vue | 65 +++++++++++++------
src/stores/user/index.ts | 11 ++++
2 files changed, 55 insertions(+), 21 deletions(-)
diff --git a/src/pages/08_request-list/RequestListView.vue b/src/pages/08_request-list/RequestListView.vue
index 9327f419..4675b124 100644
--- a/src/pages/08_request-list/RequestListView.vue
+++ b/src/pages/08_request-list/RequestListView.vue
@@ -54,6 +54,7 @@ import { Invoice } from 'src/stores/payment/types';
import { CreatedBy } from 'src/stores/types';
import { getUserId } from 'src/services/keycloak';
import { QuotationFull } from 'src/stores/quotations/types';
+import useUserStore from 'src/stores/user';
const { locale, t } = useI18n();
@@ -62,7 +63,9 @@ const route = useRoute();
const optionStore = useOptionStore();
const requestListStore = useRequestList();
const flowTemplateStore = useWorkflowTemplate();
+const userStore = useUserStore();
+const currentUserGroup = ref([]);
const workList = ref([]);
const statusFile = ref({
customer: {},
@@ -158,6 +161,10 @@ onMounted(async () => {
initTheme();
initLang();
+ const result = await userStore.fetchUserGroup();
+
+ currentUserGroup.value = result.map((v) => v.name);
+
// get data
await getData();
});
@@ -283,26 +290,38 @@ async function triggerViewFile(opt: {
if (!opt.download) window.open(url, '_blank');
}
-const responsiblePersonList = computed(() => {
- const temp = workList.value?.reduce>(
- (acc, curr: RequestWork) => {
- curr.productService.service?.workflow?.step.forEach((v) => {
- const key = v.order.toString();
+const responsibleList = computed(() => {
+ const temp = workList.value?.reduce<
+ Record
+ >((acc, curr: RequestWork) => {
+ curr.productService.service?.workflow?.step.forEach((v) => {
+ const key = v.order.toString();
+ const responsibleGroup = (
+ v.responsibleGroup as unknown as { group: string }[]
+ ).map((v) => v.group);
- if (!acc[key]) acc[key] = v.responsiblePerson.map((v) => v.user);
+ if (!acc[key]) {
+ acc[key] = {
+ user: v.responsiblePerson.map((v) => v.user),
+ group: responsibleGroup,
+ };
+ }
- const current = acc[key];
+ const current = acc[key];
- v.responsiblePerson.forEach((lhs) => {
- if (current.find((rhs) => rhs.id === lhs.userId)) return;
- current.push(lhs.user);
- });
+ v.responsiblePerson.forEach((lhs) => {
+ if (current.user.find((rhs) => rhs.id === lhs.userId)) return;
+ current.user.push(lhs.user);
});
- return acc;
- },
- {},
- );
+ responsibleGroup.forEach((lhs) => {
+ if (current.group.find((rhs) => rhs === lhs)) return;
+ current.group.push(lhs);
+ });
+ });
+
+ return acc;
+ }, {});
return temp || {};
});
@@ -496,11 +515,11 @@ function toEmployee(employee: RequestData['employee']) {
{{ $t('flow.responsiblePerson') }}
{
const data = ref>();
+ async function fetchUserGroup(id?: string) {
+ return await api
+ .get<
+ { id: string; name: string; path: string }[]
+ >(`/user/${id || getUserId()}/group`)
+ .then((res) => res.data);
+ }
+
async function fetchHqOption() {
if (userOption.value.hqOpts.length === 0) {
const res = await branchStore.fetchList({
@@ -334,6 +343,8 @@ const useUserStore = defineStore('api-user', () => {
setSignature,
typeStats,
+
+ fetchUserGroup,
};
});
From 9f6d972c910e22aafa3bb3300ae506f11fb64e0e Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 24 Apr 2025 15:16:09 +0700
Subject: [PATCH 095/331] feat: enhance AvatarGroup to display responsible
groups alongside users
---
src/components/shared/AvatarGroup.vue | 6 +-
src/pages/08_request-list/RequestListView.vue | 12 ++-
.../08_request-list/TableRequestList.vue | 80 ++++++++++++++-----
3 files changed, 74 insertions(+), 24 deletions(-)
diff --git a/src/components/shared/AvatarGroup.vue b/src/components/shared/AvatarGroup.vue
index a3f3d85e..ed8f63ef 100644
--- a/src/components/shared/AvatarGroup.vue
+++ b/src/components/shared/AvatarGroup.vue
@@ -25,7 +25,11 @@ withDefaults(
alt="Image"
/>
-
+
{{ person.name }}
diff --git a/src/pages/08_request-list/RequestListView.vue b/src/pages/08_request-list/RequestListView.vue
index 4675b124..fc6e69c2 100644
--- a/src/pages/08_request-list/RequestListView.vue
+++ b/src/pages/08_request-list/RequestListView.vue
@@ -518,8 +518,8 @@ function toEmployee(employee: RequestData['employee']) {
v-if="responsibleList[pageState.currentStep]?.user.length"
>
({
name:
$i18n.locale === 'eng'
@@ -531,8 +531,12 @@ function toEmployee(employee: RequestData['employee']) {
: `/no-img-female.png`
: `${baseUrl}/user/${v.id}/profile-image/${v.selectedImage}`,
}),
- )
- "
+ ),
+ ...responsibleList[pageState.currentStep].group.map((g) => ({
+ name: `${$t('general.group')} ${g}`,
+ imgUrl: '/img-group.png',
+ })),
+ ]"
/>
-
diff --git a/src/pages/08_request-list/TableRequestList.vue b/src/pages/08_request-list/TableRequestList.vue
index 7b99bf78..fec64111 100644
--- a/src/pages/08_request-list/TableRequestList.vue
+++ b/src/pages/08_request-list/TableRequestList.vue
@@ -42,9 +42,14 @@ defineEmits<{
const selected = defineModel('selected');
-function responsiblePerson(quotation: QuotationFull): CreatedBy[] | undefined {
+function responsiblePerson(quotation: QuotationFull) {
const productServiceList = quotation.productServiceList;
const tempPerson: CreatedBy[] = [];
+ const tempGroup: {
+ group: string;
+ id: string;
+ workflowTemplateStepId: string;
+ }[] = [];
for (const v of productServiceList) {
const tempStep = v.service?.workflow?.step;
@@ -55,7 +60,18 @@ function responsiblePerson(quotation: QuotationFull): CreatedBy[] | undefined {
tempPerson.push(rhs.user);
}
});
- return tempPerson;
+ tempStep.forEach((lhs) => {
+ const newGroup = lhs.responsibleGroup as unknown as {
+ group: string;
+ id: string;
+ workflowTemplateStepId: string;
+ }[];
+ for (const rhs of newGroup) {
+ tempGroup.push(rhs);
+ }
+ });
+ console.log({ user: tempPerson, group: tempGroup });
+ return { user: tempPerson, group: tempGroup };
}
}
@@ -261,7 +277,7 @@ function handleCheckAll() {
{{ props.row.quotation.code || '-' }}
-
+ /> -->
+
+ 0 ||
+ (responsiblePerson(props.row.quotation).group ?? []).length >
+ 0
"
+ :data="[
+ ...responsiblePerson(props.row.quotation).user.map((v) => ({
+ name:
+ $i18n.locale === 'eng'
+ ? `${v.firstNameEN} ${v.lastNameEN}`
+ : `${v.firstName} ${v.lastName}`,
+ imgUrl: !v.selectedImage
+ ? v.gender === 'male'
+ ? `/no-img-man.png`
+ : `/no-img-female.png`
+ : `${baseUrl}/user/${v.id}/profile-image/${v.selectedImage}`,
+ })),
+ ...responsiblePerson(props.row.quotation).group.map((g) => ({
+ name: `${$t('general.group')} ${g.group}`,
+ imgUrl: '/img-group.png',
+ })),
+ ]"
/>
-
From aac82ce4775f0d1efb4bca5e4ccf10b6aed1a67e Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 24 Apr 2025 15:17:46 +0700
Subject: [PATCH 096/331] refactor: remove unnecessary console log from
responsiblePerson function
---
src/pages/08_request-list/TableRequestList.vue | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/pages/08_request-list/TableRequestList.vue b/src/pages/08_request-list/TableRequestList.vue
index fec64111..72278d4e 100644
--- a/src/pages/08_request-list/TableRequestList.vue
+++ b/src/pages/08_request-list/TableRequestList.vue
@@ -70,7 +70,6 @@ function responsiblePerson(quotation: QuotationFull) {
tempGroup.push(rhs);
}
});
- console.log({ user: tempPerson, group: tempGroup });
return { user: tempPerson, group: tempGroup };
}
}
From 9a8363091d1036d48266fdfd0156ba3c9c1d4b72 Mon Sep 17 00:00:00 2001
From: Thanaphon Frappet
Date: Thu, 24 Apr 2025 15:45:02 +0700
Subject: [PATCH 097/331] refactor: delete btn paste
---
src/pages/04_product-service/MainPage.vue | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/src/pages/04_product-service/MainPage.vue b/src/pages/04_product-service/MainPage.vue
index 98146be4..aebcc54e 100644
--- a/src/pages/04_product-service/MainPage.vue
+++ b/src/pages/04_product-service/MainPage.vue
@@ -33,7 +33,6 @@ import {
SaveButton,
UndoButton,
ToggleButton,
- PasteButton,
ImportButton,
} from 'components/button';
import TableProduct from 'src/components/04_product-service/TableProduct.vue';
@@ -4554,11 +4553,7 @@ watch(
@click="serviceTreeView = false"
/>
- paste()"
- />
+
From 5c12bcbab78d666b013d47ddccab24b8af396876 Mon Sep 17 00:00:00 2001
From: Thanaphon Frappet
Date: Thu, 24 Apr 2025 15:52:15 +0700
Subject: [PATCH 098/331] refactor: trim the last 3 characters from the code
---
src/pages/04_product-service/MainPage.vue | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/pages/04_product-service/MainPage.vue b/src/pages/04_product-service/MainPage.vue
index aebcc54e..d70b6b6a 100644
--- a/src/pages/04_product-service/MainPage.vue
+++ b/src/pages/04_product-service/MainPage.vue
@@ -1848,7 +1848,7 @@ async function copy(id: string) {
const res = await fetchListServiceById(id);
if (res) {
formService.value = {
- code: res.code,
+ code: res.code.slice(0, -3),
name: res.name,
detail: res.detail,
attributes: res.attributes,
From dfc17e962369b60dde14f1e94dcf78b39c9bab52 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 24 Apr 2025 17:58:48 +0700
Subject: [PATCH 099/331] feat: update importNationality to support multiple
selections and adjust related logic
---
.../02_personnel-management/FormByType.vue | 11 ++++++-----
src/components/shared/SelectInput.vue | 2 +-
src/pages/02_personnel-management/MainPage.vue | 14 +++++++++-----
src/stores/user/types.ts | 4 ++--
4 files changed, 18 insertions(+), 13 deletions(-)
diff --git a/src/components/02_personnel-management/FormByType.vue b/src/components/02_personnel-management/FormByType.vue
index 7f850999..ec3619a1 100644
--- a/src/components/02_personnel-management/FormByType.vue
+++ b/src/components/02_personnel-management/FormByType.vue
@@ -29,7 +29,7 @@ const discountCondition = defineModel(
const sourceNationality = defineModel(
'sourceNationality',
);
-const importNationality = defineModel(
+const importNationality = defineModel(
'importNationality',
);
const trainingPlace = defineModel('trainingPlace');
@@ -179,18 +179,19 @@ function deleteFile(name: string) {
(v) => (typeof v === 'string' ? (sourceNationality = v) : '')
"
/>
+
(typeof v === 'string' ? (importNationality = v) : '')
- "
/>
{
- model = '';
+ multiple ? (model = []) : (model = '');
}
"
>
diff --git a/src/pages/02_personnel-management/MainPage.vue b/src/pages/02_personnel-management/MainPage.vue
index b3ea5058..b0aa4f02 100644
--- a/src/pages/02_personnel-management/MainPage.vue
+++ b/src/pages/02_personnel-management/MainPage.vue
@@ -99,7 +99,7 @@ const userFileList = ref<{ name: string; url: string }[]>([]);
const typeStats = ref();
const userStats = ref();
-const searchDate = ref([]);
+const searchDate = ref<[]>([]);
const urlProfile = ref();
const profileFileImg = ref(null);
@@ -126,7 +126,7 @@ const defaultFormData = {
streetEN: '',
street: '',
trainingPlace: null,
- importNationality: null,
+ importNationality: [],
sourceNationality: null,
licenseExpireDate: null,
licenseIssueDate: null,
@@ -178,7 +178,7 @@ const formData = ref({
streetEN: '',
street: '',
trainingPlace: null,
- importNationality: null,
+ importNationality: [],
sourceNationality: null,
licenseExpireDate: null,
licenseIssueDate: null,
@@ -555,6 +555,7 @@ async function triggerChangeStatus(id: string, status: string) {
async function assignFormData(idEdit: string) {
if (!userData.value) return;
const foundUser = userData.value.result.find((user) => user.id === idEdit);
+ console.log(foundUser);
if (foundUser) {
currentUser.value = foundUser;
@@ -576,7 +577,10 @@ async function assignFormData(idEdit: string) {
street: foundUser.street,
streetEN: foundUser.streetEN,
trainingPlace: foundUser.trainingPlace,
- importNationality: foundUser.importNationality,
+ importNationality:
+ typeof foundUser.importNationality === 'string'
+ ? [foundUser.importNationality]
+ : foundUser.importNationality,
sourceNationality: foundUser.sourceNationality,
licenseNo: foundUser.licenseNo,
discountCondition: foundUser.discountCondition,
@@ -750,7 +754,7 @@ watch(
formData.value.responsibleArea = null;
formData.value.discountCondition = null;
formData.value.sourceNationality = null;
- formData.value.importNationality = null;
+ formData.value.importNationality = [];
formData.value.trainingPlace = null;
formData.value.checkpoint = null;
formData.value.checkpointEN = null;
diff --git a/src/stores/user/types.ts b/src/stores/user/types.ts
index 28dcfc8b..2f6daeb5 100644
--- a/src/stores/user/types.ts
+++ b/src/stores/user/types.ts
@@ -13,7 +13,7 @@ export type User = {
createdBy: string;
status: Status;
trainingPlace: string | null;
- importNationality: string | null;
+ importNationality: [] | null;
sourceNationality: string | null;
licenseExpireDate: Date | null;
licenseIssueDate: Date | null;
@@ -82,7 +82,7 @@ export type UserCreate = {
streetEN: string;
street: string;
trainingPlace?: string | null;
- importNationality?: string | null;
+ importNationality?: string[] | null;
sourceNationality?: string | null;
licenseExpireDate?: Date | null;
licenseIssueDate?: Date | null;
From ef815225611558fe6522a00109ee083011bca161 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Thu, 24 Apr 2025 17:59:11 +0700
Subject: [PATCH 100/331] feat: add QR code upload functionality and enhance
bank management logic
---
.../01_branch-management/FormBank.vue | 2 +-
.../07_agencies-management/AgenciesDialog.vue | 95 ++++++++++++++++++-
src/pages/07_agencies-management/MainPage.vue | 31 +++---
src/stores/institution/index.ts | 51 +++++++++-
4 files changed, 157 insertions(+), 22 deletions(-)
diff --git a/src/components/01_branch-management/FormBank.vue b/src/components/01_branch-management/FormBank.vue
index 66f4241b..8c2b53bd 100644
--- a/src/components/01_branch-management/FormBank.vue
+++ b/src/components/01_branch-management/FormBank.vue
@@ -177,7 +177,7 @@ watch(
('imageListOnCreate', { default: { selectedImage: '', list: [] } });
+const deletesStatusQrCodeBankImag = defineModel(
+ 'deletesStatusQrCodeBankImag',
+ { default: [] },
+);
const imageState = reactive({
imageDialog: false,
@@ -45,6 +50,14 @@ const imageState = reactive({
const imageFile = ref(null);
const imageList = ref<{ selectedImage: string; list: string[] }>();
+const qrCodeDialog = ref(false);
+const qrCodeImageUrl = ref('');
+const currentIndexQrCodeBank = ref(-1);
+const statusQrCodeFile = ref(undefined);
+const refQrCodeUpload = ref();
+const statusQrCodeUrl = ref('');
+const statusDeletesQrCode = ref(false);
+
const props = withDefaults(
defineProps<{
readonly?: boolean;
@@ -163,6 +176,35 @@ function clearImageState() {
imageState.refreshImageState = false;
}
+function triggerEditQrCodeBank(opts?: { save?: boolean }) {
+ if (opts?.save === undefined) {
+ qrCodeDialog.value = true;
+ statusDeletesQrCode.value = false;
+ statusQrCodeUrl.value =
+ formBankBook.value[currentIndexQrCodeBank.value].bankUrl || '';
+ statusDeletesQrCode.value = false;
+ } else {
+ formBankBook.value[currentIndexQrCodeBank.value].bankUrl =
+ statusQrCodeUrl.value;
+
+ formBankBook.value[currentIndexQrCodeBank.value].bankQr =
+ statusQrCodeFile.value;
+
+ if (statusDeletesQrCode.value === true) {
+ deletesStatusQrCodeBankImag.value.push(currentIndexQrCodeBank.value);
+ }
+ if (statusDeletesQrCode.value === false) {
+ deletesStatusQrCodeBankImag.value =
+ deletesStatusQrCodeBankImag.value.filter(
+ (item) => item !== currentIndexQrCodeBank.value,
+ );
+ }
+
+ currentIndexQrCodeBank.value = -1;
+ statusDeletesQrCode.value = false;
+ }
+}
+
watch(
() => imageFile.value,
() => {
@@ -177,7 +219,6 @@ watch(
imageList.value
? (imageList.value.selectedImage = data.value.selectedImage || '')
: '';
- console.log(imageState.imageUrl);
imageState.refreshImageState = false;
},
);
@@ -327,8 +368,19 @@ watch(
title="agencies.bankInfo"
class="q-pt-xl"
dense
- single
v-model:bank-book-list="formBankBook"
+ @view-qr="
+ (i) => {
+ currentIndexQrCodeBank = i;
+ triggerEditQrCodeBank();
+ }
+ "
+ @edit-qr="
+ (i) => {
+ currentIndexQrCodeBank = i;
+ refQrCodeUpload && refQrCodeUpload.browse();
+ }
+ "
/>
@@ -518,9 +570,20 @@ watch(
title="agencies.bankInfo"
class="q-pt-xl"
dense
- single
:readonly
v-model:bank-book-list="formBankBook"
+ @view-qr="
+ (i) => {
+ currentIndexQrCodeBank = i;
+ triggerEditQrCodeBank();
+ }
+ "
+ @edit-qr="
+ (i) => {
+ currentIndexQrCodeBank = i;
+ refQrCodeUpload && refQrCodeUpload.browse();
+ }
+ "
/>
@@ -561,5 +624,31 @@ watch(
+
+ {
+ qrCodeDialog = false;
+ if (currentIndexQrCodeBank !== -1) {
+ triggerEditQrCodeBank({ save: true });
+ }
+ }
+ "
+ @clear="statusDeletesQrCode = true"
+ clearButton
+ >
+
+
+
+
+
+
diff --git a/src/pages/07_agencies-management/MainPage.vue b/src/pages/07_agencies-management/MainPage.vue
index 9f40a0c4..13d6158e 100644
--- a/src/pages/07_agencies-management/MainPage.vue
+++ b/src/pages/07_agencies-management/MainPage.vue
@@ -81,7 +81,7 @@ const pageState = reactive({
isDrawerEdit: true,
searchDate: [],
});
-
+const deletesStatusQrCodeBankImag = ref([]);
const blankFormData: InstitutionPayload = {
group: '',
code: '',
@@ -177,16 +177,15 @@ function assignFormData(data: Institution) {
contactEmail: data.contactEmail,
contactName: data.contactName,
contactTel: data.contactTel,
- bank: [
- {
- bankName: data.bank[0]?.bankName,
- accountNumber: data.bank[0]?.accountNumber,
- bankBranch: data.bank[0]?.bankBranch,
- accountName: data.bank[0]?.accountName,
- accountType: data.bank[0]?.accountType,
- currentlyUse: data.bank[0]?.currentlyUse,
- },
- ],
+ bank: data.bank.map((v) => ({
+ bankName: v.bankName,
+ accountNumber: v.accountNumber,
+ bankBranch: v.bankBranch,
+ accountName: v.accountName,
+ accountType: v.accountType,
+ currentlyUse: v.currentlyUse,
+ bankUrl: `${baseUrl}/institution/${data.id}/bank-qr/${v.id}?ts=${Date.now()}`,
+ })),
};
}
@@ -212,14 +211,10 @@ async function submit(opt?: { selectedImage: string }) {
provinceId: formData.value.provinceId,
status: formData.value.status,
bank: formData.value.bank.map((v) => ({
- bankName: v.bankName,
- accountNumber: v.accountNumber,
- bankBranch: v.bankBranch,
- accountName: v.accountName,
- accountType: v.accountType,
- currentlyUse: v.currentlyUse,
+ ...v,
})),
};
+ console.log('payload', payload);
if (
(pageState.isDrawerEdit && currAgenciesData.value?.id) ||
(opt?.selectedImage && currAgenciesData.value?.id)
@@ -230,6 +225,7 @@ async function submit(opt?: { selectedImage: string }) {
id: currAgenciesData.value.id,
selectedImage: opt?.selectedImage || undefined,
}),
+ { indexDeleteQrCodeBank: deletesStatusQrCodeBankImag.value },
);
if (ret) {
@@ -982,6 +978,7 @@ watch(
v-model:data="formData"
v-model:form-bank-book="formData.bank"
v-model:image-list-on-create="imageListOnCreate"
+ v-model:deletes-status-qr-code-bank-imag="deletesStatusQrCodeBankImag"
/>
From 84d3e0d7777af1c50f817ca7fc9646615b83dc69 Mon Sep 17 00:00:00 2001
From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com>
Date: Fri, 25 Apr 2025 17:00:16 +0700
Subject: [PATCH 104/331] fix: dark mode manual table
---
src/pages/00_manual/ViewPage.vue | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/pages/00_manual/ViewPage.vue b/src/pages/00_manual/ViewPage.vue
index bac74c09..05e08147 100644
--- a/src/pages/00_manual/ViewPage.vue
+++ b/src/pages/00_manual/ViewPage.vue
@@ -339,12 +339,12 @@ async function scrollTo(id: string) {
}
.markdown :deep(:where(table th)) {
- background: #f3f3f7;
- border: 1px solid #e1e1e8;
+ background: var(--surface-2);
+ border: 1px solid var(--border-color);
}
.markdown :deep(:where(table td, table th)) {
- border: 1px solid #e1e1e8;
+ border: 1px solid var(--border-color);
padding: 0.25rem 1rem;
}
From d67b9b2f02a055736d5f484f9898e5d8dfcbec92 Mon Sep 17 00:00:00 2001
From: Thanaphon Frappet
Date: Mon, 28 Apr 2025 10:32:50 +0700
Subject: [PATCH 105/331] refactor: edit i18n
---
src/i18n/tha.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/i18n/tha.ts b/src/i18n/tha.ts
index 31adb4c6..f01b2dca 100644
--- a/src/i18n/tha.ts
+++ b/src/i18n/tha.ts
@@ -587,7 +587,7 @@ export default {
family: 'ข้อมูลครอบครัว',
},
workerStatus: 'สถานะคนงาน',
- previousPassportNumber: 'หมายเลขอันเก่าหนังสือเดินทาง',
+ previousPassportNumber: 'หมายเลขหนังสือเดินทางเล่มเก่า',
employerBranch: 'สาขานายจ้าง',
employeeCode: 'รหัสลูกจ้าง',
nrcNo: 'เลขบัตรประจำตัวคนซึ่งไม่มีสัญชาติไทย (N.R.C No.)',
From 93700a2b542ea1a07c5f9ee0608df19c5efb5209 Mon Sep 17 00:00:00 2001
From: Thanaphon Frappet
Date: Mon, 28 Apr 2025 13:49:49 +0700
Subject: [PATCH 106/331] refactor: edit i18n
---
src/i18n/tha.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/i18n/tha.ts b/src/i18n/tha.ts
index f01b2dca..6bf8c591 100644
--- a/src/i18n/tha.ts
+++ b/src/i18n/tha.ts
@@ -616,7 +616,7 @@ export default {
visa90Day: 'รายงานตัว 90 วัน ครั้งต่อไป',
arrivalCardNo: 'บัตรขาเข้า หมายเลข TM',
visaCheckpoint: 'เดินทางเข้ามาทางด่าน',
- workerType: 'ประเภทคนงาน',
+ workerType: 'สถานะคนงาน',
placeOfBirth: 'สถานที่เกิด',
countryOfbirth: 'ประเทศที่เกิด',
issueCountry: 'ประเทศที่ออก',
From 02c7598aec12278813a023caa788d665baf40236 Mon Sep 17 00:00:00 2001
From: Thanaphon Frappet
Date: Mon, 28 Apr 2025 13:57:04 +0700
Subject: [PATCH 107/331] refactor: edit label
---
src/components/03_customer-management/FormEmployeePassport.vue | 2 +-
src/i18n/tha.ts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/03_customer-management/FormEmployeePassport.vue b/src/components/03_customer-management/FormEmployeePassport.vue
index 5fe21b52..c87f13dd 100644
--- a/src/components/03_customer-management/FormEmployeePassport.vue
+++ b/src/components/03_customer-management/FormEmployeePassport.vue
@@ -258,7 +258,7 @@ watch(
:options="workerStatusOptions"
:hide-dropdown-icon="readonly"
:for="`${prefixId}-select-visa-type`"
- :label="$t('customerEmployee.form.workerType')"
+ :label="$t('customerEmployee.form.workerStatus')"
@filter="workerStatusFilter"
:model-value="readonly ? workerStatus || '-' : workerStatus"
@update:model-value="
diff --git a/src/i18n/tha.ts b/src/i18n/tha.ts
index 6bf8c591..f01b2dca 100644
--- a/src/i18n/tha.ts
+++ b/src/i18n/tha.ts
@@ -616,7 +616,7 @@ export default {
visa90Day: 'รายงานตัว 90 วัน ครั้งต่อไป',
arrivalCardNo: 'บัตรขาเข้า หมายเลข TM',
visaCheckpoint: 'เดินทางเข้ามาทางด่าน',
- workerType: 'สถานะคนงาน',
+ workerType: 'ประเภทคนงาน',
placeOfBirth: 'สถานที่เกิด',
countryOfbirth: 'ประเทศที่เกิด',
issueCountry: 'ประเทศที่ออก',
From 46de2412dfd401401a6e18d1189530d2c683a8c3 Mon Sep 17 00:00:00 2001
From: Thanaphon Frappet
Date: Tue, 29 Apr 2025 11:22:10 +0700
Subject: [PATCH 108/331] refactor: handle role
---
src/pages/04_product-service/MainPage.vue | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/pages/04_product-service/MainPage.vue b/src/pages/04_product-service/MainPage.vue
index d70b6b6a..88d6746c 100644
--- a/src/pages/04_product-service/MainPage.vue
+++ b/src/pages/04_product-service/MainPage.vue
@@ -2506,6 +2506,7 @@ watch(
/>
Date: Tue, 29 Apr 2025 13:07:55 +0700
Subject: [PATCH 109/331] feat: auto go into first entry if only one
---
src/pages/00_manual/MainPage.vue | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/src/pages/00_manual/MainPage.vue b/src/pages/00_manual/MainPage.vue
index a0b85ef8..33a19ebe 100644
--- a/src/pages/00_manual/MainPage.vue
+++ b/src/pages/00_manual/MainPage.vue
@@ -10,10 +10,11 @@ import { onMounted, watch } from 'vue';
import { useManualStore } from 'src/stores/manual';
import { useNavigator } from 'src/stores/navigator';
import { Icon } from '@iconify/vue/dist/iconify.js';
-import { useRoute } from 'vue-router';
+import { useRoute, useRouter } from 'vue-router';
// NOTE: Variable
const route = useRoute();
+const router = useRouter();
const manualStore = useManualStore();
const navigatorStore = useNavigator();
const { dataManual, dataTroubleshooting } = storeToRefs(manualStore);
@@ -33,6 +34,16 @@ watch(
if (route.name === 'Troubleshooting') {
const res = await manualStore.getTroubleshooting();
dataTroubleshooting.value = res ? res : [];
+ if (
+ res.length &&
+ res.length === 1 &&
+ res[0].page &&
+ res[0].page.length === 1
+ ) {
+ router.replace(
+ `/troubleshooting/${res[0].category}/${res[0].page[0].name}`,
+ );
+ }
}
},
{ immediate: true },
From c4c4b76973aa1dd6fb32486c7531732e09f7f522 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Wed, 30 Apr 2025 10:23:40 +0700
Subject: [PATCH 110/331] refactor: adjust button sizes and padding for
improved layout consistency
---
src/pages/00_notification/MainPage.vue | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/pages/00_notification/MainPage.vue b/src/pages/00_notification/MainPage.vue
index a3506813..a2a37e65 100644
--- a/src/pages/00_notification/MainPage.vue
+++ b/src/pages/00_notification/MainPage.vue
@@ -94,21 +94,21 @@ onMounted(async () => {
-
+
+
await fetchNoti()"
>
Refresh
@@ -119,8 +119,8 @@ onMounted(async () => {
rounded
flat
dense
- size="xs"
- class="app-text-muted-2 q-ml-sm"
+ size="sm"
+ class="app-text-muted-2"
@click="async () => await deleteNoti()"
>
{{ $t('general.delete') }}
@@ -131,7 +131,7 @@ onMounted(async () => {
rounded
flat
dense
- size="xs"
+ size="sm"
class="app-text-muted-2 q-mx-sm"
@click="async () => await markAsRead()"
>
From 7a2be56ef4668357d8633fc93656ab7c992a91e3 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Wed, 30 Apr 2025 11:13:45 +0700
Subject: [PATCH 111/331] refactor: improve checkbox and badge behavior based
on notification state
---
src/pages/00_notification/MainPage.vue | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/src/pages/00_notification/MainPage.vue b/src/pages/00_notification/MainPage.vue
index a2a37e65..bc4cee01 100644
--- a/src/pages/00_notification/MainPage.vue
+++ b/src/pages/00_notification/MainPage.vue
@@ -97,7 +97,8 @@ onMounted(async () => {
@@ -177,7 +178,13 @@ onMounted(async () => {
{{
tab.value === 'all'
From 0871a8b899706e3dc1a4ca145c35934674f7bf98 Mon Sep 17 00:00:00 2001
From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com>
Date: Wed, 30 Apr 2025 12:02:05 +0700
Subject: [PATCH 112/331] feat: only allow current responsible user and group
---
src/pages/08_request-list/RequestListView.vue | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/pages/08_request-list/RequestListView.vue b/src/pages/08_request-list/RequestListView.vue
index fc6e69c2..ad7254fe 100644
--- a/src/pages/08_request-list/RequestListView.vue
+++ b/src/pages/08_request-list/RequestListView.vue
@@ -1005,7 +1005,21 @@ function toEmployee(employee: RequestData['employee']) {
:label="$t('dialog.action.close')"
/>
Date: Wed, 30 Apr 2025 12:04:18 +0700
Subject: [PATCH 113/331] feat: enable responsible user filtering and enhance
button states for improved UX
---
src/pages/08_request-list/MainPage.vue | 1 +
src/pages/08_request-list/RequestListAction .vue | 2 ++
2 files changed, 3 insertions(+)
diff --git a/src/pages/08_request-list/MainPage.vue b/src/pages/08_request-list/MainPage.vue
index 7f98b870..64b339dc 100644
--- a/src/pages/08_request-list/MainPage.vue
+++ b/src/pages/08_request-list/MainPage.vue
@@ -143,6 +143,7 @@ async function openRequestListDialog() {
page: 1,
pageSize: 999,
incomplete: true,
+ responsibleOnly: true,
});
if (ret) {
diff --git a/src/pages/08_request-list/RequestListAction .vue b/src/pages/08_request-list/RequestListAction .vue
index 7ec34ff4..09f62aa1 100644
--- a/src/pages/08_request-list/RequestListAction .vue
+++ b/src/pages/08_request-list/RequestListAction .vue
@@ -153,6 +153,7 @@ watch(
@click="prev"
/>
Date: Wed, 30 Apr 2025 15:11:12 +0700
Subject: [PATCH 114/331] refactor: remove unused workerList and reset
selectedWorker on branch change
---
src/pages/05_quotation/QuotationForm.vue | 18 ++++++------------
1 file changed, 6 insertions(+), 12 deletions(-)
diff --git a/src/pages/05_quotation/QuotationForm.vue b/src/pages/05_quotation/QuotationForm.vue
index d7e332f7..fdc36d8b 100644
--- a/src/pages/05_quotation/QuotationForm.vue
+++ b/src/pages/05_quotation/QuotationForm.vue
@@ -198,7 +198,6 @@ const selectedWorkerItem = computed(() => {
})),
];
});
-const workerList = ref([]);
const firstCodePayment = ref('');
const selectedProductGroup = ref('');
const selectedInstallmentNo = ref([]);
@@ -1059,12 +1058,7 @@ watch(
() => quotationFormData.value.customerBranchId,
async (v) => {
if (!v) return;
-
- const retEmp = await customerStore.fetchBranchEmployee(v, {
- passport: true,
- });
-
- if (retEmp) workerList.value = retEmp.data.result;
+ selectedWorker.value = [];
},
);
@@ -2531,8 +2525,8 @@ function covertToNode() {
}
:deep(
- i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-item__toggle-icon--rotated
- ) {
+ i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-item__toggle-icon--rotated
+) {
color: var(--brand-1);
}
@@ -2549,9 +2543,9 @@ function covertToNode() {
}
:deep(
- .q-item.q-item-type.row.no-wrap.q-item--dense.q-item--clickable.q-link.cursor-pointer.q-focusable.q-hoverable.surface-1
- .q-focus-helper
- ) {
+ .q-item.q-item-type.row.no-wrap.q-item--dense.q-item--clickable.q-link.cursor-pointer.q-focusable.q-hoverable.surface-1
+ .q-focus-helper
+) {
visibility: hidden;
}
From 70e9755952d1f7a6f91ed6e602681bd040bc60d8 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Wed, 30 Apr 2025 15:34:30 +0700
Subject: [PATCH 115/331] fix: ensure contactName and contactTel default to
empty string if undefined
---
src/pages/02_personnel-management/MainPage.vue | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/pages/02_personnel-management/MainPage.vue b/src/pages/02_personnel-management/MainPage.vue
index b0aa4f02..d5a54507 100644
--- a/src/pages/02_personnel-management/MainPage.vue
+++ b/src/pages/02_personnel-management/MainPage.vue
@@ -600,8 +600,8 @@ async function assignFormData(idEdit: string) {
responsibleArea: foundUser.responsibleArea,
status: foundUser.status,
selectedImage: foundUser.selectedImage,
- contactName: foundUser.contactName,
- contactTel: foundUser.contactTel,
+ contactName: foundUser.contactName || '',
+ contactTel: foundUser.contactTel || '',
licenseExpireDate:
(foundUser.licenseExpireDate &&
new Date(foundUser.licenseExpireDate)) ||
From 4d023a7c7c5e5ed6b50277205ee6619d1413509a Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Wed, 30 Apr 2025 16:15:08 +0700
Subject: [PATCH 116/331] feat: add smooth scrolling on validation error for
forms
---
src/components/DialogForm.vue | 10 ++++++++++
src/components/DrawerInfo.vue | 10 ++++++++++
src/components/dialog/DialogFormContainer.vue | 10 ++++++++++
src/pages/02_personnel-management/MainPage.vue | 1 -
4 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/src/components/DialogForm.vue b/src/components/DialogForm.vue
index 84319bcf..fb29a4ca 100644
--- a/src/components/DialogForm.vue
+++ b/src/components/DialogForm.vue
@@ -42,6 +42,15 @@ defineProps<{
const modal = defineModel('modal', { default: false });
const currentTab = defineModel('currentTab');
+
+async function onValidationError(ref: any) {
+ const el = ref.$el as Element;
+ el.scrollIntoView({
+ behavior: 'smooth',
+ block: 'center',
+ inline: 'nearest',
+ });
+}
('currentTab');
@submit.prevent
@validation-success="submit"
class="column full-height"
+ @validation-error="onValidationError"
>
@@ -41,6 +50,7 @@ const state = defineModel({ default: false });
}"
>
$emit('submit', e)"
@reset="$emit('reset')"
greedy
diff --git a/src/pages/02_personnel-management/MainPage.vue b/src/pages/02_personnel-management/MainPage.vue
index d5a54507..cff14f9d 100644
--- a/src/pages/02_personnel-management/MainPage.vue
+++ b/src/pages/02_personnel-management/MainPage.vue
@@ -555,7 +555,6 @@ async function triggerChangeStatus(id: string, status: string) {
async function assignFormData(idEdit: string) {
if (!userData.value) return;
const foundUser = userData.value.result.find((user) => user.id === idEdit);
- console.log(foundUser);
if (foundUser) {
currentUser.value = foundUser;
From 2b78abcd3bd5dbb911212b453fcda504be61e7b9 Mon Sep 17 00:00:00 2001
From: puriphatt
Date: Wed, 30 Apr 2025 16:58:40 +0700
Subject: [PATCH 117/331] fix: adjust layout classes for responsive design on
ViewPage
---
src/pages/00_manual/ViewPage.vue | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/pages/00_manual/ViewPage.vue b/src/pages/00_manual/ViewPage.vue
index 05e08147..074a49cf 100644
--- a/src/pages/00_manual/ViewPage.vue
+++ b/src/pages/00_manual/ViewPage.vue
@@ -116,8 +116,8 @@ async function scrollTo(id: string) {
- |