jws-frontend/src/pages/04_product-service/MainPage.vue

2408 lines
66 KiB
Vue
Raw Normal View History

2024-06-10 16:42:59 +07:00
<script setup lang="ts">
import { ref, watch } from 'vue';
2024-06-28 14:38:16 +07:00
import { useI18n } from 'vue-i18n';
import { onMounted } from 'vue';
import { storeToRefs } from 'pinia';
import ItemCard from 'components/ItemCard.vue';
2024-06-10 16:42:59 +07:00
import AppBox from 'components/app/AppBox.vue';
2024-06-12 15:37:17 +07:00
import AddButton from 'components/AddButton.vue';
2024-06-28 14:14:23 +07:00
import ProductCardComponent from 'components/04_product-service/ProductCardComponent.vue';
2024-06-10 16:42:59 +07:00
import StatCard from 'components/StatCardComponent.vue';
2024-06-28 14:14:23 +07:00
import DrawerInfo from 'components/DrawerInfo.vue';
import BasicInformation from 'components/04_product-service/BasicInformation.vue';
import FormDialog from 'components/FormDialog.vue';
2024-06-12 15:37:17 +07:00
import TooltipComponent from 'components/TooltipComponent.vue';
2024-06-28 14:14:23 +07:00
import ButtonAddComponent from 'components/ButtonAddCompoent.vue';
import BasicInfoProduct from 'components/04_product-service/BasicInfoProduct.vue';
import PriceDataComponent from 'components/04_product-service/PriceDataComponent.vue';
import ProfileUpload from 'components/ProfileUpload.vue';
import TotalProductCardComponent from 'components/04_product-service/TotalProductCardComponent.vue';
2024-06-28 14:14:23 +07:00
import FormServiceWork from 'components/04_product-service/FormServiceWork.vue';
import ServiceProperties from 'components/04_product-service/ServiceProperties.vue';
import WorkNameManagement from 'components/04_product-service/WorkNameManagement.vue';
2024-06-18 11:47:53 +00:00
import useOptionStore from 'src/stores/options';
2024-06-28 14:14:23 +07:00
import FormServiceProperties from 'components/04_product-service/FormServiceProperties.vue';
import InfoForm from 'components/02_personnel-management/InfoForm.vue';
2024-06-28 14:38:16 +07:00
import NoData from 'components/NoData.vue';
import PaginationComponent from 'src/components/PaginationComponent.vue';
2024-06-14 09:31:45 +07:00
2024-06-26 17:21:26 +07:00
import useFlowStore from 'src/stores/flow';
import useMyBranchStore from 'src/stores/my-branch';
const useMyBranch = useMyBranchStore();
const { fetchListOptionBranch } = useMyBranch;
2024-06-14 09:31:45 +07:00
import { Status } from 'src/stores/types';
2024-06-26 17:21:26 +07:00
2024-07-02 09:58:59 +00:00
import useUtilsStore, { dialog, dialogWarningClose } from 'src/stores/utils';
2024-06-11 15:04:01 +07:00
import useProductServiceStore from 'src/stores/product-service';
2024-06-14 15:18:59 +07:00
import {
ProductGroup,
ProductGroupCreate,
ProductCreate,
ProductList,
ServiceCreate,
Service,
ServiceById,
2024-06-20 18:02:02 +07:00
WorkItems,
Attributes,
ServiceAndProduct,
2024-06-14 15:18:59 +07:00
} from 'src/stores/product-service/types';
2024-06-11 15:04:01 +07:00
2024-06-28 14:38:16 +07:00
const flowStore = useFlowStore();
2024-07-02 09:58:59 +00:00
const utilsStore = useUtilsStore();
2024-06-11 15:04:01 +07:00
const productServiceStore = useProductServiceStore();
2024-06-18 11:47:53 +00:00
const optionStore = useOptionStore();
2024-06-11 15:04:01 +07:00
const {
fetchStatsProductType,
2024-06-12 15:37:17 +07:00
createProductServiceType,
editProductServiceType,
deleteProductServiceType,
fetchListProductServiceType,
2024-06-11 15:04:01 +07:00
fetchStatsProductGroup,
fetchListProductService,
createProductService,
editProductService,
deleteProductService,
fetchStatsProduct,
fetchListProduct,
createProduct,
editProduct,
deleteProduct,
fetchStatsService,
fetchListService,
fetchListServiceById,
createService,
2024-06-18 16:14:50 +07:00
deleteService,
editService,
createWork,
editWork,
deleteWork,
fetchListProductAndService,
2024-06-11 15:04:01 +07:00
} = productServiceStore;
const { workNameItems } = storeToRefs(productServiceStore);
2024-06-10 16:42:59 +07:00
const stat = ref<
{
2024-07-03 14:13:22 +07:00
icon: string;
2024-06-10 16:42:59 +07:00
count: number;
label: string;
2024-06-12 15:37:17 +07:00
color: 'pink' | 'purple' | 'green' | 'orange';
2024-06-10 16:42:59 +07:00
}[]
>([
2024-07-03 14:13:22 +07:00
{ icon: 'mdi-folder', count: 0, label: 'productAndService', color: 'pink' },
{
icon: 'mdi-folder',
count: 0,
label: 'productAndServiceType',
color: 'purple',
},
{ icon: 'mdi-folder', count: 0, label: 'service', color: 'orange' },
{ icon: 'mdi-folder', count: 0, label: 'product', color: 'green' },
2024-06-10 16:42:59 +07:00
]);
const { t } = useI18n();
const baseUrl = ref<string>(import.meta.env.VITE_API_BASE_URL);
2024-06-28 14:38:16 +07:00
const inputSearch = ref('');
const inputSearchProductAndService = ref('');
2024-06-19 18:00:38 +07:00
2024-06-28 14:38:16 +07:00
const currentStatusProduct = ref(false);
const drawerInfo = ref(false);
const isEdit = ref(false);
2024-06-28 14:38:16 +07:00
const dialogInputForm = ref(false);
const dialogProduct = ref(false);
const dialogService = ref(false);
const dialogProductEdit = ref(false);
const dialogServiceEdit = ref(false);
2024-06-28 14:38:16 +07:00
const statusToggle = ref(false);
const profileSubmit = ref(false);
const infoProductEdit = ref(false);
const infoServiceEdit = ref(false);
const imageProduct = ref<File | undefined>(undefined);
2024-06-14 14:45:17 +07:00
const profileUrl = ref<string | null>('');
2024-06-11 16:52:35 +07:00
2024-06-28 14:38:16 +07:00
const pathGroupName = ref('');
const pathTypeName = ref('');
2024-06-20 18:02:02 +07:00
2024-06-28 14:38:16 +07:00
const dialogProductServiceType = ref(false);
const dialogTotalProduct = ref(false);
const productMode = ref<'group' | 'type' | 'service' | 'product'>('group');
2024-06-14 15:18:59 +07:00
const productGroup = ref<ProductGroup[]>();
const productType = ref<ProductGroup[]>();
const product = ref<(ProductList & { type: 'product' })[]>();
const productIsAdd = ref<(ProductList & { type: 'product' })[]>();
const service = ref<(Service & { type: 'service' })[]>();
const resultSearchProduct = ref<ProductList[]>();
const productAndService = ref<ServiceAndProduct[]>([]);
2024-06-28 14:38:16 +07:00
const productAndServiceTab = ref<'all' | 'product' | 'service'>('all');
const manageWorkNameDialog = ref(false);
const previousValue = ref();
const propertiesOption = ref();
const formDataGroup = ref<ProductGroupCreate>({
remark: '',
detail: '',
name: '',
code: '',
});
const formDataProduct = ref<ProductCreate>({
registeredBranchId: '',
productTypeId: '',
remark: '',
serviceCharge: 0,
agentPrice: 0,
price: 0,
process: 0,
detail: '',
name: '',
code: '',
image: undefined,
});
const formDataProductService = ref<ServiceCreate>({
work: [],
attributes: {
additional: [],
},
detail: '',
name: '',
code: '',
productTypeId: '',
registeredBranchId: '',
});
2024-06-18 02:01:02 +00:00
const serviceTab = [
{
name: 'serviceInformation',
label: 'serviceInformation',
},
{
name: 'workInformation',
label: 'workInformation',
},
];
2024-06-20 18:02:02 +07:00
const workItems = ref<WorkItems[]>([]);
2024-06-21 11:06:35 +00:00
const workNameRef = ref();
const selectProduct = ref<ProductList[]>([]);
const currentWorkIndex = ref<number>(0);
2024-06-20 18:02:02 +07:00
2024-06-18 02:01:02 +00:00
const currentServiceTab = ref('serviceInformation');
2024-06-18 11:47:53 +00:00
const propertiesDialog = ref<boolean>(false);
2024-06-18 02:01:02 +00:00
const totalProduct = ref<number>(0);
const totalService = ref<number>(0);
const totalProductAndService = ref<number>(0);
// แบ่งหน้า
const currentPageGroup = ref<number>(1);
const maxPageGroup = ref<number>(1);
2024-06-26 13:12:41 +07:00
const pageSizeGroup = ref<number>(30);
const currentPageType = ref<number>(1);
const maxPageType = ref<number>(1);
const pageSizeType = ref<number>(30);
const maxPageServiceAndProduct = ref<number>(1);
const pageSizeServiceAndProduct = ref<number>(10);
const currentPageServiceAndProduct = ref<number>(1);
const totalType = ref<number>(0);
const totalGroup = ref<number>(0);
const total = ref<number>(0);
2024-06-26 11:26:29 +07:00
// เก็บ id ที่เข้ามา
const currentId = ref<string>('');
2024-06-12 15:37:17 +07:00
const currentIdType = ref<string>('');
const currentIdService = ref<string>('');
const currentIdProduct = ref<string>('');
const branchOption = ref<{ id: string; name: string }[]>([]);
const currentStatus = ref<Status | 'All'>('All');
2024-06-14 14:45:17 +07:00
const inputFile = (() => {
const element = document.createElement('input');
element.type = 'file';
element.accept = 'image/*';
const reader = new FileReader();
reader.addEventListener('load', () => {
if (typeof reader.result === 'string') profileUrl.value = reader.result;
});
element.addEventListener('change', () => {
imageProduct.value = element.files?.[0];
if (imageProduct.value) {
reader.readAsDataURL(imageProduct.value);
2024-06-14 14:45:17 +07:00
}
});
return element;
})();
async function searchProduct(isAdd: boolean = true) {
const res = await fetchListProduct({
query: inputSearchProductAndService.value,
});
if (res) {
if (isAdd) {
resultSearchProduct.value = res.result;
}
if (!isAdd) {
product.value = res.result.map((v) => {
return {
...v,
type: 'product',
};
});
}
}
2024-06-26 17:21:26 +07:00
flowStore.rotate();
}
async function featchStatsService() {
const resStatsService = await fetchStatsService();
totalService.value = resStatsService;
2024-06-26 17:21:26 +07:00
flowStore.rotate();
}
async function fetchListOfOptionBranch() {
const res = await fetchListOptionBranch({ pageSize: 999 });
if (res) branchOption.value = res.result;
}
async function featchStatsProduct() {
const resStatsProduct = await fetchStatsProduct({
productTypeId: currentIdType.value,
});
totalProduct.value = resStatsProduct;
2024-06-26 17:21:26 +07:00
flowStore.rotate();
}
2024-06-12 15:37:17 +07:00
async function fetchListType() {
const res = await fetchListProductServiceType({
page: currentPageType.value,
pageSize: pageSizeType.value,
query: !!inputSearch.value ? inputSearch.value : undefined,
2024-06-12 15:37:17 +07:00
productGroupId: currentId.value,
status:
currentStatus.value === 'All'
? undefined
: currentStatus.value === 'ACTIVE'
? 'ACTIVE'
: 'INACTIVE',
2024-06-12 15:37:17 +07:00
});
if (res) {
currentPageType.value = res.page;
totalType.value = res.total;
maxPageType.value = Math.ceil(res.total / pageSizeType.value);
2024-06-26 11:26:29 +07:00
productType.value = res.result;
2024-06-12 15:37:17 +07:00
}
}
async function fetchListGroups() {
const res = await fetchListProductService({
page: currentPageGroup.value,
pageSize: pageSizeGroup.value,
query: !!inputSearch.value ? inputSearch.value : undefined,
status:
currentStatus.value === 'All'
? undefined
: currentStatus.value === 'ACTIVE'
? 'ACTIVE'
: 'INACTIVE',
});
2024-06-12 15:37:17 +07:00
if (res) {
currentPageGroup.value = res.page;
totalGroup.value = res.total;
maxPageGroup.value = Math.ceil(res.total / pageSizeGroup.value);
2024-06-26 11:26:29 +07:00
productGroup.value = res.result;
2024-06-12 15:37:17 +07:00
}
}
async function fetchListOfProductIsAdd(productTypeId: string) {
const res = await fetchListProduct({
status:
currentStatus.value === 'INACTIVE'
? 'INACTIVE'
: currentStatus.value === 'ACTIVE'
? 'ACTIVE'
: undefined,
productTypeId,
});
if (res) {
productIsAdd.value = res.result.map((v) => {
return {
...v,
type: 'product',
};
});
}
}
async function fetchListOfProduct() {
const res = await fetchListProduct({
page: currentPageServiceAndProduct.value,
pageSize: pageSizeServiceAndProduct.value,
query: !!inputSearchProductAndService.value
? inputSearchProductAndService.value
: undefined,
status:
currentStatus.value === 'INACTIVE'
? 'INACTIVE'
: currentStatus.value === 'ACTIVE'
? 'ACTIVE'
: undefined,
productTypeId: currentIdType.value,
});
if (res) {
total.value = res.total;
totalProduct.value = res.total;
currentPageServiceAndProduct.value = res.page;
maxPageServiceAndProduct.value = Math.ceil(
res.total / pageSizeServiceAndProduct.value,
);
product.value = res.result.map((v) => {
return {
...v,
type: 'product',
};
});
}
}
async function fetchListOfService() {
const res = await fetchListService({
page: currentPageServiceAndProduct.value,
query: !!inputSearchProductAndService.value
? inputSearchProductAndService.value
: undefined,
pageSize: pageSizeServiceAndProduct.value,
status:
currentStatus.value === 'INACTIVE'
? 'INACTIVE'
: currentStatus.value === 'ACTIVE'
? 'ACTIVE'
: undefined,
productTypeId: currentIdType.value,
});
if (res) {
totalService.value = res.total;
total.value = res.total;
currentPageServiceAndProduct.value = res.page;
maxPageServiceAndProduct.value = Math.ceil(
res.total / pageSizeServiceAndProduct.value,
);
service.value = res.result.map((v) => {
return {
...v,
type: 'service',
};
});
}
}
async function submitType() {
2024-06-12 15:37:17 +07:00
if (drawerInfo.value) {
await editProductServiceType(currentIdType.value, {
...formDataGroup.value,
2024-06-12 15:37:17 +07:00
productGroupId: currentId.value,
});
drawerInfo.value = false;
} else {
dialogInputForm.value = false;
const res = await createProductServiceType(
currentId.value,
formDataGroup.value,
);
if (res) {
stat.value[1].count = stat.value[1].count + 1;
}
2024-06-12 15:37:17 +07:00
}
await fetchListType();
}
const itemCard = [
{
icon: 'mdi:server-network',
text: 'บริการ',
color: 'var(--orange-5)',
},
{
icon: 'mdi:shopping',
text: 'สินค้า',
color: 'var(--green-9)',
},
];
async function toggleStatusProduct(id: string, status: Status) {
await editProduct(id, {
status: status === 'INACTIVE' ? 'ACTIVE' : 'INACTIVE',
});
await fetchListOfProduct();
2024-06-26 17:21:26 +07:00
flowStore.rotate();
}
async function toggleStatusService(id: string, status: Status) {
await editService(id, {
status: status === 'INACTIVE' ? 'ACTIVE' : 'INACTIVE',
});
await fetchListOfService();
2024-06-26 17:21:26 +07:00
flowStore.rotate();
}
2024-06-19 18:00:38 +07:00
async function toggleStatusType(id: string, status: Status) {
await editProductServiceType(id, {
status: status === 'INACTIVE' ? 'ACTIVE' : 'INACTIVE',
});
await fetchListType();
2024-06-26 17:21:26 +07:00
flowStore.rotate();
2024-06-19 18:00:38 +07:00
}
async function toggleStatusGroup(id: string, status: Status) {
await editProductService(id, {
status: status === 'INACTIVE' ? 'ACTIVE' : 'INACTIVE',
});
await fetchListGroups();
2024-06-26 17:21:26 +07:00
flowStore.rotate();
2024-06-19 18:00:38 +07:00
}
async function fetchListOfProductAndService() {
const res = await fetchListProductAndService({
page: currentPageServiceAndProduct.value,
pageSize: pageSizeServiceAndProduct.value,
query: !!inputSearchProductAndService.value
? inputSearchProductAndService.value
: undefined,
status:
currentStatus.value === 'INACTIVE'
? 'INACTIVE'
: currentStatus.value === 'ACTIVE'
? 'ACTIVE'
: undefined,
productTypeId: currentIdType.value,
});
if (res) {
if (currentStatus.value === 'All') {
totalProductAndService.value = res.total;
}
total.value = res.total;
currentPageServiceAndProduct.value = res.page;
maxPageServiceAndProduct.value = Math.ceil(
res.total / pageSizeServiceAndProduct.value,
);
productAndService.value = res.result;
}
}
2024-06-18 16:14:50 +07:00
async function deleteServiceById(serviceId?: string) {
dialog({
color: 'negative',
icon: 'mdi-alert',
title: t('deleteConfirmTitle'),
actionText: t('ok'),
persistent: true,
message: t('deleteConfirmMessage'),
action: async () => {
const res = await deleteService(serviceId ?? currentIdService.value);
2024-06-18 16:14:50 +07:00
if (productAndServiceTab.value === 'service') {
await await fetchListOfService();
}
if (productAndServiceTab.value === 'all') {
await fetchListOfProductAndService();
}
dialogServiceEdit.value = false;
if (res) {
totalService.value = totalService.value - 1;
}
2024-06-26 17:21:26 +07:00
flowStore.rotate();
calculateStats();
2024-06-18 16:14:50 +07:00
},
cancel: () => {},
});
}
async function deleteTypeOfProduct(id?: string) {
dialog({
color: 'negative',
icon: 'mdi-alert',
title: t('deleteConfirmTitle'),
actionText: t('ok'),
persistent: true,
message: t('deleteConfirmMessage'),
action: async () => {
const res = await deleteProduct(id ?? currentIdProduct.value);
if (productAndServiceTab.value === 'product') {
await fetchListOfProduct();
}
if (productAndServiceTab.value === 'all') {
await fetchListOfProductAndService();
}
dialogProductEdit.value = false;
if (res) {
totalProduct.value = totalProduct.value - 1;
}
2024-06-26 17:21:26 +07:00
flowStore.rotate();
calculateStats();
},
cancel: () => {},
});
}
// type or group
async function deleteProductById(productId?: string) {
dialog({
color: 'negative',
icon: 'mdi-alert',
title: t('deleteConfirmTitle'),
actionText: t('ok'),
persistent: true,
message: t('deleteConfirmMessage'),
action: async () => {
2024-06-11 16:52:35 +07:00
if (productMode.value === 'type') {
// Product Type
await deleteProductServiceType(productId ?? currentIdType.value);
2024-06-12 15:37:17 +07:00
await fetchListType();
}
if (productMode.value === 'group') {
// Product Group
const res = await deleteProductService(productId ?? currentId.value);
2024-06-12 15:37:17 +07:00
if (res) {
}
await fetchListGroups();
}
2024-06-26 17:21:26 +07:00
flowStore.rotate();
calculateStats();
drawerInfo.value = false;
},
cancel: () => {},
});
}
2024-06-11 15:04:01 +07:00
function undoProductGroup() {
formDataGroup.value = {
remark: previousValue.value.remark,
detail: previousValue.value.detail,
name: previousValue.value.name,
code: previousValue.value.code,
};
isEdit.value = false;
2024-06-26 17:21:26 +07:00
flowStore.rotate();
}
function assignFormDataGroup(data: ProductGroup) {
previousValue.value = data;
formDataGroup.value = {
remark: data.remark,
detail: data.detail,
name: data.name,
code: data.code,
};
}
2024-06-24 16:53:33 +07:00
const prevService = ref<ServiceCreate>({
work: [],
attributes: {
additional: [],
},
detail: '',
name: '',
code: '',
productTypeId: '',
registeredBranchId: '',
2024-06-24 16:53:33 +07:00
});
2024-06-26 17:44:13 +07:00
const currentService = ref<ServiceById>();
async function assignFormDataProductService(id: string) {
const res = await fetchListServiceById(id);
if (res) {
2024-06-24 16:53:33 +07:00
statusToggle.value = res.status === 'INACTIVE' ? false : true;
2024-07-01 11:04:24 +00:00
profileUrl.value = res.imageUrl;
profileSubmit.value = true;
2024-06-26 17:44:13 +07:00
currentService.value = JSON.parse(JSON.stringify(res));
2024-06-24 16:53:33 +07:00
prevService.value = {
code: res.code,
name: res.name,
detail: res.detail,
attributes: res.attributes,
work: [],
2024-06-24 16:25:33 +07:00
status: res.status,
productTypeId: res.productTypeId,
registeredBranchId: res.registeredBranchId,
};
2024-06-24 16:53:33 +07:00
formDataProductService.value = { ...prevService.value };
res.work.forEach((item) => {
2024-06-24 16:53:33 +07:00
prevService.value.work.push({
name: item.name,
attributes: item.attributes,
productId: item.productOnWork.map(
(productOnWorkItem) => productOnWorkItem.product.id,
),
});
});
2024-06-24 16:53:33 +07:00
formDataProductService.value.work = prevService.value.work;
workItems.value = res.work.map((item) => {
return {
id: item.id,
name: item.name,
attributes: item.attributes,
product: item.productOnWork.map((productOnWorkItem) => {
return {
...productOnWorkItem.product,
2024-06-26 17:44:13 +07:00
nameEn: productOnWorkItem.product.name,
};
}),
};
});
}
}
const prevProduct = ref<ProductCreate>({
productTypeId: '',
remark: '',
serviceCharge: 0,
agentPrice: 0,
price: 0,
process: 0,
detail: '',
name: '',
code: '',
image: undefined,
registeredBranchId: '',
});
function assignFormDataProduct(data: ProductList) {
statusToggle.value = data.status === 'INACTIVE' ? false : true;
console.log(data);
profileUrl.value = `${baseUrl.value}/product/${data?.id}/image`;
profileSubmit.value = true;
prevProduct.value = {
productTypeId: data.productTypeId,
remark: data.remark,
serviceCharge: data.serviceCharge,
agentPrice: data.agentPrice,
price: data.price,
process: data.process,
detail: data.detail === '' ? '-' : data.detail,
name: data.name,
code: data.code,
image: undefined,
registeredBranchId: data.registeredBranchId,
};
formDataProduct.value = { ...prevProduct.value };
}
function clearFormGroup() {
formDataGroup.value = {
2024-06-11 15:04:01 +07:00
remark: '',
detail: '',
name: '',
code: '',
};
dialogInputForm.value = false;
}
function clearFormProduct() {
formDataProduct.value = {
productTypeId: '',
remark: '',
serviceCharge: 0,
agentPrice: 0,
price: 0,
process: 0,
detail: '',
name: '',
code: '',
image: undefined,
registeredBranchId: '',
};
2024-07-01 11:04:24 +00:00
imageProduct.value = undefined;
dialogProduct.value = false;
dialogProductEdit.value = false;
2024-07-01 08:52:56 +00:00
profileUrl.value = '';
profileSubmit.value = false;
}
function clearFormService() {
formDataProductService.value = {
code: '',
name: '',
detail: '',
attributes: {
additional: [],
},
work: [],
2024-06-24 16:25:33 +07:00
status: undefined,
productTypeId: '',
registeredBranchId: '',
};
2024-06-21 11:40:18 +07:00
workItems.value = [];
2024-06-21 11:40:18 +07:00
selectProduct.value = [];
dialogService.value = false;
dialogServiceEdit.value = false;
2024-07-01 08:52:56 +00:00
profileUrl.value = '';
profileSubmit.value = false;
2024-07-01 11:04:24 +00:00
imageProduct.value = undefined;
}
2024-06-21 11:40:18 +07:00
function assignFormDataProductServiceCreate() {
formDataProductService.value.work = [];
2024-06-21 11:40:18 +07:00
workItems.value.forEach((item) => {
formDataProductService.value.work.push({
name: item.name,
attributes: item.attributes,
productId: item.product.map((productItem) => productItem.id),
});
});
}
async function submitService() {
2024-06-21 11:40:18 +07:00
assignFormDataProductServiceCreate();
formDataProductService.value.image = imageProduct.value;
formDataProductService.value.productTypeId = currentIdType.value;
if (dialogService.value) {
formDataProductService.value.productTypeId = currentIdType.value;
if (formDataProductService.value.registeredBranchId === '') {
formDataProductService.value.registeredBranchId = null;
}
await createService(formDataProductService.value);
totalService.value = totalService.value + 1;
}
if (dialogServiceEdit.value) {
await editService(currentIdService.value, {
...formDataProductService.value,
2024-06-24 16:53:33 +07:00
status: statusToggle.value
? formDataProductService.value.status
: 'INACTIVE',
});
}
if (productAndServiceTab.value === 'all') {
await fetchListOfProductAndService();
}
if (productAndServiceTab.value === 'service') {
await fetchListOfService();
}
2024-06-26 17:21:26 +07:00
flowStore.rotate();
clearFormService();
}
async function submitProduct() {
formDataProduct.value.productTypeId = currentIdType.value;
if (profileSubmit.value) {
formDataProduct.value.image = imageProduct.value;
}
if (dialogProduct.value) {
const res = await createProduct(formDataProduct.value);
if (res) {
stat.value[3].count = stat.value[3].count + 1;
}
}
if (dialogProductEdit.value) {
await editProduct(currentIdProduct.value, {
...formDataProduct.value,
status: statusToggle.value ? 'ACTIVE' : 'INACTIVE',
});
}
totalProduct.value = totalProduct.value + 1;
clearFormProduct();
if (productAndServiceTab.value === 'all') {
await fetchListOfProductAndService();
}
if (productAndServiceTab.value === 'product') {
await fetchListOfProduct();
}
2024-06-26 17:21:26 +07:00
flowStore.rotate();
}
2024-06-11 15:04:01 +07:00
async function submitGroup() {
if (drawerInfo.value) {
await editProductService(currentId.value, formDataGroup.value);
} else {
const res = await createProductService(formDataGroup.value);
2024-06-12 15:37:17 +07:00
if (res) {
stat.value[0].count = stat.value[0].count + 1;
}
}
2024-06-11 15:04:01 +07:00
2024-06-14 09:31:45 +07:00
drawerInfo.value = false;
await fetchListGroups();
clearFormGroup();
2024-06-26 17:21:26 +07:00
flowStore.rotate();
2024-06-11 15:04:01 +07:00
}
2024-06-21 11:06:35 +00:00
function confirmDeleteWork(id: string) {
dialog({
color: 'negative',
icon: 'mdi-alert',
title: t('deleteConfirmTitle'),
actionText: t('delete'),
message: t('deleteConfirmMessage'),
action: async () => {
deleteWork(id);
2024-06-26 17:21:26 +07:00
flowStore.rotate();
2024-06-21 11:06:35 +00:00
},
cancel: () => {},
});
}
function triggerConfirmCloseWork() {
const isWorkNameEdit =
workNameRef.value && workNameRef.value.isWorkNameEdit
? workNameRef.value.isWorkNameEdit()
: false;
if (isWorkNameEdit) {
dialogWarningClose(t, {
action: () => {
manageWorkNameDialog.value = false;
},
});
} else {
manageWorkNameDialog.value = false;
}
2024-06-21 11:06:35 +00:00
}
2024-06-20 18:02:02 +07:00
const tempValueProperties = ref<Attributes>({
additional: [],
});
const currentPropertiesMode = ref<'service' | 'work'>('service');
function openPropertiesDialog(type: 'service' | 'work') {
if (type === 'service') {
2024-06-24 07:21:36 +00:00
propertiesOption.value = optionStore.globalOption.servicePropertiesField;
}
if (type === 'work') {
2024-06-24 07:21:36 +00:00
propertiesOption.value = optionStore.globalOption.workPropertiesField;
}
2024-06-20 18:02:02 +07:00
currentPropertiesMode.value = type;
propertiesDialog.value = true;
}
async function calculateStats() {
2024-06-12 15:37:17 +07:00
const resStatsGroup = await fetchStatsProductGroup();
const resStatsType = await fetchStatsProductType();
const resStatsService = await fetchStatsService();
const resStatsProduct = await fetchStatsProduct();
2024-06-12 15:37:17 +07:00
stat.value[0].count = resStatsGroup ?? 0;
stat.value[1].count = resStatsType ?? 0;
stat.value[2].count = resStatsService ?? 0;
stat.value[3].count = resStatsProduct ?? 0;
}
2024-06-12 15:37:17 +07:00
async function fetchStatus() {
if (productAndServiceTab.value === 'all') {
await fetchListOfProductAndService();
2024-06-26 17:21:26 +07:00
flowStore.rotate();
}
if (productAndServiceTab.value === 'product') {
await fetchListOfProduct();
2024-06-26 17:21:26 +07:00
flowStore.rotate();
}
if (productAndServiceTab.value === 'service') {
await fetchListOfService();
2024-06-26 17:21:26 +07:00
flowStore.rotate();
}
}
async function alternativeFetch() {
if (productAndServiceTab.value === 'all') {
await fetchListOfProductAndService();
flowStore.rotate();
}
if (productAndServiceTab.value === 'product') {
await fetchListOfProduct();
flowStore.rotate();
}
if (productAndServiceTab.value === 'service') {
await fetchListOfService();
flowStore.rotate();
}
}
2024-06-26 17:44:13 +07:00
function cloneData() {
if (!currentService.value) return;
formDataProductService.value = {
...prevService.value,
attributes: JSON.parse(JSON.stringify(currentService.value.attributes)),
};
workItems.value = currentService.value.work.map((item) => {
return {
id: item.id,
name: item.name,
attributes: JSON.parse(JSON.stringify(item.attributes)),
product: item.productOnWork.map((productOnWorkItem) => {
return {
...productOnWorkItem.product,
nameEn: productOnWorkItem.product.name,
};
}),
};
});
}
onMounted(async () => {
2024-07-02 09:58:59 +00:00
utilsStore.currentTitle.title = 'mainProductTitle';
utilsStore.currentTitle.caption = 'mainProductCaption';
calculateStats();
await fetchListGroups();
2024-06-26 17:21:26 +07:00
flowStore.rotate();
2024-06-11 15:04:01 +07:00
});
watch(productMode, () => {
inputSearch.value = '';
currentStatus.value = 'All';
});
2024-06-14 09:31:45 +07:00
2024-06-14 13:37:11 +07:00
watch(currentStatus, async () => {
if (productMode.value === 'group') {
await fetchListGroups();
} else {
await fetchListType();
}
2024-06-26 17:21:26 +07:00
flowStore.rotate();
2024-06-14 13:37:11 +07:00
});
watch(inputSearch, async () => {
if (productMode.value === 'group') {
await fetchListGroups();
flowStore.rotate();
}
if (productMode.value === 'type') {
await fetchListType();
flowStore.rotate();
}
});
watch(inputSearchProductAndService, async () => {
await alternativeFetch();
});
2024-06-10 16:42:59 +07:00
</script>
<template>
2024-06-14 09:31:45 +07:00
<ButtonAddComponent style="z-index: 999">
<q-fab-action
v-if="productMode === 'group'"
2024-06-14 13:37:11 +07:00
id="btn-add-product-group"
2024-06-14 09:31:45 +07:00
style="color: white; background-color: hsla(var(--pink-6-hsl))"
padding="xs"
icon="mdi-folder-multiple-plus"
:label="$t('productAndService')"
external-label
label-position="left"
@click="
() => {
clearFormGroup();
2024-06-14 09:31:45 +07:00
dialogInputForm = true;
}
"
/>
<q-fab-action
v-if="productMode === 'type'"
2024-06-14 13:37:11 +07:00
id="btn-add-product-type"
2024-06-14 09:31:45 +07:00
:label="$t('productAndServiceType')"
external-label
label-position="left"
style="color: white; background-color: hsla(var(--purple-11-hsl))"
padding="xs"
icon="mdi-folder-multiple-plus"
@click="
() => {
clearFormGroup();
2024-06-14 09:31:45 +07:00
dialogInputForm = true;
}
"
></q-fab-action>
2024-06-14 14:45:17 +07:00
<q-fab-action
v-if="productMode === 'service'"
:label="$t('buttonAddProduct')"
2024-06-14 14:45:17 +07:00
external-label
label-position="left"
style="color: white; background-color: hsla(var(--green-11-hsl))"
padding="xs"
icon="mdi-folder-multiple-plus"
@click="
() => {
clearFormProduct();
fetchListOfOptionBranch();
2024-06-14 14:45:17 +07:00
dialogProduct = true;
}
"
></q-fab-action>
<q-fab-action
v-if="productMode === 'service'"
2024-06-18 02:01:02 +00:00
:label="$t('addService')"
2024-06-14 14:45:17 +07:00
external-label
label-position="left"
style="color: white; background-color: hsla(var(--orange-6-hsl))"
2024-06-14 14:45:17 +07:00
padding="xs"
icon="mdi-folder-multiple-plus"
@click="
() => {
clearFormGroup();
2024-07-01 08:52:56 +00:00
clearFormService();
fetchListOfOptionBranch();
2024-06-18 02:01:02 +00:00
currentServiceTab = 'serviceInformation';
dialogService = true;
2024-06-14 14:45:17 +07:00
}
"
></q-fab-action>
2024-06-14 09:31:45 +07:00
</ButtonAddComponent>
2024-07-02 09:58:59 +00:00
<div class="column full-height no-wrap">
2024-07-04 04:12:10 +00:00
<div
v-if="stat[0].count === 0"
2024-07-04 04:12:10 +00:00
class="column col bordered rounded q-pa-md"
2024-06-12 15:37:17 +07:00
:no-padding="productGroup?.length !== 0"
2024-06-11 16:52:35 +07:00
>
<TooltipComponent
class="self-end"
title="messageTooltipNoProduct"
caption="messageTooltipProductCreate"
imgSrc="personnel-table-"
/>
<div class="row items-center justify-center" style="flex-grow: 1">
<AddButton
label="productCreate"
@trigger="() => (dialogInputForm = true)"
2024-06-12 15:37:17 +07:00
/>
</div>
2024-07-04 04:12:10 +00:00
</div>
2024-06-12 15:37:17 +07:00
2024-07-02 09:58:59 +00:00
<div v-if="stat[0].count !== 0" class="column full-height no-wrap">
2024-06-12 15:37:17 +07:00
<div
class="row items-center q-mb-md"
2024-06-28 14:14:23 +07:00
v-if="productMode === 'service' || productMode === 'type'"
2024-06-12 15:37:17 +07:00
>
<q-btn
round
icon="mdi-arrow-left"
flat
dense
2024-06-28 14:14:23 +07:00
@click="productMode = productMode === 'service' ? 'type' : 'group'"
2024-06-12 15:37:17 +07:00
class="q-mr-md"
/>
2024-06-28 14:14:23 +07:00
<div
class="text-h6 hover-underline"
:class="{
2024-06-28 14:38:24 +07:00
'app-text-muted': productMode !== 'type',
'text-weight-bold': productMode === 'type',
2024-06-28 14:14:23 +07:00
}"
@click="productMode = 'type'"
>
2024-06-20 18:02:02 +07:00
{{ pathGroupName }}
2024-06-12 15:37:17 +07:00
</div>
2024-06-28 14:14:23 +07:00
<div
class="text-h6 app-text-muted q-mx-sm"
v-if="productMode === 'service'"
>
/
2024-06-10 16:42:59 +07:00
</div>
2024-06-28 14:14:23 +07:00
<div
v-if="productMode === 'service'"
class="text-h6"
:class="{
'app-text-muted': productMode !== 'service',
'text-weight-bold': productMode === 'service',
}"
>
{{ pathTypeName }}
2024-06-12 15:37:17 +07:00
</div>
</div>
2024-07-04 04:14:41 +00:00
<div
2024-06-12 15:37:17 +07:00
v-if="productMode === 'group' || productMode === 'type'"
2024-07-04 13:14:21 +07:00
class="scroll q-mb-md"
2024-06-12 15:37:17 +07:00
>
2024-07-04 13:14:21 +07:00
<div style="display: inline-block">
2024-07-04 04:14:41 +00:00
<StatCard label-i18n :branch="stat" :dark="$q.dark.isActive" nowrap />
</div>
</div>
2024-07-02 09:58:59 +00:00
<div
2024-06-12 15:37:17 +07:00
v-if="productMode === 'group' || productMode === 'type'"
2024-07-02 09:58:59 +00:00
class="surface-1 col bordered rounded q-pa-md scroll"
2024-06-12 15:37:17 +07:00
>
<div class="row justify-between">
<div
v-if="productMode === 'type'"
class="text-h6 text-weight-bold q-mb-md"
>
{{ $t('productAndServiceType') }}
</div>
<div
v-else-if="productMode === 'group'"
class="text-h6 text-weight-bold q-mb-md"
>
{{ $t('productAndServiceAll') }}
</div>
<div
v-if="productMode === 'group' || productMode === 'type'"
class="row q-px-md q-pb-md"
>
<q-input
style="width: 300px"
outlined
dense
:label="$t('search')"
class="q-mr-lg"
:bg-color="$q.dark.isActive ? 'dark' : 'white'"
v-model="inputSearch"
debounce="500"
>
<template v-slot:prepend>
<q-icon name="mdi-magnify" />
</template>
</q-input>
<q-btn
icon="mdi-tune-vertical-variant"
size="sm"
:color="$q.dark.isActive ? 'dark' : 'white'"
:text-color="$q.dark.isActive ? 'white' : 'dark'"
class="bordered rounded"
unelevated
>
<q-menu class="bordered">
<q-list v-close-popup dense>
<q-item
clickable
class="flex items-center"
@click="
() => {
currentStatus = 'All';
}
"
2024-06-12 15:37:17 +07:00
>
{{ $t('all') }}
</q-item>
<q-item
clickable
class="flex items-center"
@click="
() => {
currentStatus = 'ACTIVE';
}
"
2024-06-12 15:37:17 +07:00
>
{{ $t('statusACTIVE') }}
</q-item>
<q-item
clickable
class="flex items-center"
@click="
() => {
currentStatus = 'INACTIVE';
}
"
2024-06-12 15:37:17 +07:00
>
{{ $t('statusINACTIVE') }}
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
</div>
2024-06-12 15:37:17 +07:00
<div class="row q-col-gutter-lg">
<div
:class="`${$q.screen.gt.sm ? 'col-3' : $q.screen.gt.xs ? 'col-6' : 'col-12'}`"
v-for="(v, i) in productMode === 'type'
? productType
: productGroup"
2024-06-12 15:37:17 +07:00
:key="i"
>
<ProductCardComponent
v-if="productMode === 'type'"
2024-06-14 16:36:28 +07:00
isType
:count-product="v._count.product"
:count-type="v._count.type"
2024-07-03 06:52:55 +00:00
:count-service="v._count.service"
:title="v.name"
2024-06-12 15:37:17 +07:00
:subtitle="v.code"
:date="new Date(v.updatedAt)"
:status="v.status"
2024-06-19 18:00:38 +07:00
:id="v.id"
:isDisabled="v.status === 'INACTIVE'"
:color="
$q.dark.isActive
? 'var(--purple-7-hsl)'
: 'var(--violet-11-hsl)'
"
2024-06-19 18:00:38 +07:00
@toggleStatus="toggleStatusType(v.id, v.status)"
@viewCard="
2024-06-12 15:37:17 +07:00
() => {
currentStatusProduct = v.status === 'INACTIVE';
clearFormGroup();
2024-06-12 15:37:17 +07:00
currentIdType = v.id;
assignFormDataGroup(v);
2024-06-12 15:37:17 +07:00
isEdit = false;
drawerInfo = true;
}
"
@updateCard="
2024-06-12 15:37:17 +07:00
() => {
clearFormGroup();
currentIdType = v.id;
assignFormDataGroup(v);
isEdit = true;
drawerInfo = true;
}
"
@deleteCard="
() => {
deleteProductById(v.id);
}
"
@on-click="
async () => {
2024-06-20 18:02:02 +07:00
pathTypeName = v.name;
2024-06-12 15:37:17 +07:00
currentIdType = v.id;
productMode = 'service';
2024-07-03 11:00:12 +00:00
productAndServiceTab = 'all';
// await featchStatsService();
// await featchStatsProduct();
await fetchListOfProduct();
await fetchListOfService();
await fetchListOfProductAndService();
2024-06-26 17:21:26 +07:00
flowStore.rotate();
2024-06-12 15:37:17 +07:00
}
"
/>
<ProductCardComponent
v-else-if="productMode === 'group'"
:title="v.name"
:subtitle="v.code"
:count-product="v._count.product"
:count-type="v._count.type"
2024-07-03 06:52:55 +00:00
:count-service="v._count.service"
2024-06-12 15:37:17 +07:00
:date="new Date(v.updatedAt)"
:status="v.status"
2024-06-14 16:36:28 +07:00
color="var(--pink-6-hsl)"
2024-06-19 18:00:38 +07:00
:id="v.id"
:isDisabled="v.status === 'INACTIVE'"
2024-06-19 18:00:38 +07:00
@toggleStatus="toggleStatusGroup(v.id, v.status)"
@viewCard="
2024-06-12 15:37:17 +07:00
() => {
currentStatusProduct = v.status === 'INACTIVE';
clearFormGroup();
assignFormDataGroup(v);
2024-06-12 15:37:17 +07:00
isEdit = false;
currentId = v.id;
drawerInfo = true;
}
"
@updateCard="
() => {
clearFormGroup();
assignFormDataGroup(v);
isEdit = true;
currentId = v.id;
2024-06-12 15:37:17 +07:00
drawerInfo = true;
}
"
@deleteCard="
() => {
deleteProductById(v.id);
}
"
2024-06-12 15:37:17 +07:00
@on-click="
async () => {
2024-06-20 18:02:02 +07:00
pathGroupName = v.name;
2024-06-12 15:37:17 +07:00
currentId = v.id;
productMode = 'type';
await fetchListType();
2024-06-26 17:21:26 +07:00
flowStore.rotate();
2024-06-12 15:37:17 +07:00
}
"
/>
</div>
</div>
<div
v-if="
(productMode === 'type' && productType?.length === 0) ||
(productGroup?.length === 0 && productMode === 'group')
"
class="flex justify-center items-center q-mt-lg"
style="min-height: 70vh; background-color: var(--surface-2)"
>
<NoData :not-found="!!inputSearch" />
</div>
2024-06-28 14:14:23 +07:00
<template v-else>
<div
v-if="productMode === 'group'"
class="flex items-center justify-between q-pt-xl"
2024-06-28 14:14:23 +07:00
>
<div class="col-4">
<div class="row items-center">
<div class="app-text-muted" style="width: 80px">
{{ $t('showing') }}
</div>
<div>
<q-btn-dropdown dense unelevated :label="pageSizeGroup">
<q-list>
<q-item
v-for="v in [10, 30, 50, 100, 500, 1000]"
2024-07-05 02:42:09 +00:00
:key="v"
clickable
v-close-popup
@click="
async () => {
pageSizeGroup = v;
await fetchListGroups();
}
"
>
<q-item-section>
<q-item-label>{{ v }}</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div>
</div>
<div class="col-4 row justify-center app-text-muted">
{{
$t('recordsPage', {
resultcurrentPage: productGroup?.length,
total: totalGroup,
})
}}
</div>
<div class="col-4 row justify-end">
<PaginationComponent
v-model:current-page="currentPageGroup"
v-model:max-page="maxPageGroup"
:fetch-data="
async () => {
await fetchListGroups();
flowStore.rotate();
}
"
/>
</div>
2024-06-28 14:14:23 +07:00
</div>
2024-06-26 11:26:29 +07:00
2024-06-28 14:14:23 +07:00
<div
v-if="productMode === 'type'"
class="flex justify-between q-pt-xl"
2024-06-28 14:14:23 +07:00
>
<div class="col-4">
<div class="row items-center">
<div class="app-text-muted" style="width: 80px">
{{ $t('showing') }}
</div>
<div>
<q-btn-dropdown dense unelevated :label="pageSizeType">
<q-list>
<q-item
v-for="v in [10, 30, 50, 100, 500, 1000]"
2024-07-05 02:42:09 +00:00
:key="v"
clickable
v-close-popup
@click="
async () => {
pageSizeType = v;
await fetchListType();
}
"
>
<q-item-section>
<q-item-label>{{ v }}</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div>
</div>
<div class="col-4 row justify-center app-text-muted">
{{
$t('recordsPage', {
resultcurrentPage: productType?.length,
total: totalType,
})
}}
</div>
<div class="col-4 row justify-end">
<PaginationComponent
v-model:current-page="currentPageType"
v-model:max-page="maxPageType"
:fetch-data="
async () => {
await fetchListType();
flowStore.rotate();
}
"
/>
</div>
2024-06-28 14:14:23 +07:00
</div>
</template>
2024-07-02 09:58:59 +00:00
</div>
<div
v-else-if="productMode === 'service'"
class="surface-1 col bordered rounded scroll"
>
<div class="row justify-between q-px-md">
<q-tabs v-model="productAndServiceTab" dense>
<q-tab
name="all"
@click="
async () => {
currentPageServiceAndProduct = 1;
2024-06-26 11:26:29 +07:00
inputSearchProductAndService = '';
currentStatus = 'All';
pageSizeServiceAndProduct;
2024-07-03 11:00:12 +00:00
// await fetchListOfProductAndService();
2024-06-26 17:21:26 +07:00
flowStore.rotate();
}
"
>
<div
class="row"
:class="
productAndServiceTab === 'all'
? 'text-bold'
: 'app-text-muted'
"
>
2024-06-20 18:02:02 +07:00
{{ $t('all') }}
<q-badge
class="rounded q-ml-sm"
:class="
productAndServiceTab === 'all' ? '' : 'app-text-muted'
"
:color="productAndServiceTab === 'all' ? 'primary' : ''"
style="background-color: var(--surface-3)"
:label="totalProductAndService"
/>
</div>
</q-tab>
<q-tab
name="product"
@click="
async () => {
currentPageServiceAndProduct = 1;
2024-06-26 11:26:29 +07:00
inputSearchProductAndService = '';
currentStatus = 'All';
2024-07-03 11:00:12 +00:00
// await fetchListOfProduct();
2024-06-26 17:21:26 +07:00
flowStore.rotate();
}
"
>
<div
class="row"
:class="
productAndServiceTab === 'product'
? 'text-bold'
: 'app-text-muted'
"
>
2024-06-26 11:26:29 +07:00
{{ $t('product') }}
<q-badge
class="rounded q-ml-sm"
:class="
productAndServiceTab === 'product' ? '' : 'app-text-muted'
"
:color="productAndServiceTab === 'product' ? 'primary' : ''"
style="background-color: var(--surface-3)"
:label="totalProduct"
/>
</div>
</q-tab>
<q-tab
name="service"
@click="
async () => {
currentPageServiceAndProduct = 1;
2024-06-26 11:26:29 +07:00
inputSearchProductAndService = '';
currentStatus = 'All';
2024-07-03 11:00:12 +00:00
// await fetchListOfService();
2024-06-26 17:21:26 +07:00
flowStore.rotate();
}
"
>
<div
class="row"
:class="
productAndServiceTab === 'service'
? 'text-bold'
: 'app-text-muted'
"
>
2024-06-26 11:26:29 +07:00
{{ $t('service') }}
<q-badge
class="rounded q-ml-sm"
:class="
productAndServiceTab === 'service' ? '' : 'app-text-muted'
"
:color="productAndServiceTab === 'service' ? 'primary' : ''"
style="background-color: var(--surface-3)"
:label="totalService"
/>
</div>
</q-tab>
</q-tabs>
<div class="row q-pt-sm q-mb-sm">
<q-input
style="width: 300px"
outlined
dense
unelavated
:label="$t('search')"
class="q-mr-md"
:bg-color="$q.dark.isActive ? 'dark' : 'white'"
v-model="inputSearchProductAndService"
2024-06-26 11:26:29 +07:00
debounce="250"
>
<template v-slot:prepend>
<q-icon name="mdi-magnify" />
</template>
</q-input>
<q-btn
icon="mdi-tune-vertical-variant"
size="sm"
:color="$q.dark.isActive ? 'dark' : 'white'"
:text-color="$q.dark.isActive ? 'white' : 'dark'"
class="bordered rounded q-py-sm"
unelevated
>
<q-menu class="bordered">
<q-list v-close-popup dense>
<q-item
clickable
class="flex items-center"
@click="
async () => {
currentStatus = 'All';
await fetchStatus();
}
"
>
{{ $t('all') }}
</q-item>
<q-item
clickable
class="flex items-center"
@click="
async () => {
currentStatus = 'ACTIVE';
await fetchStatus();
}
"
>
{{ $t('statusACTIVE') }}
</q-item>
<q-item
clickable
class="flex items-center"
@click="
async () => {
currentStatus = 'INACTIVE';
await fetchStatus();
}
"
>
{{ $t('statusINACTIVE') }}
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
</div>
2024-06-11 16:52:35 +07:00
<div
2024-06-26 11:26:29 +07:00
v-if="
(productAndServiceTab === 'all' &&
productAndService?.length === 0) ||
(productAndServiceTab === 'product' && product?.length === 0) ||
(productAndServiceTab === 'service' && service?.length === 0)
"
class="flex justify-center items-center"
style="min-height: 70vh; background-color: var(--surface-2)"
2024-06-11 16:52:35 +07:00
>
<NoData :not-found="!!inputSearchProductAndService" />
2024-06-10 17:42:37 +07:00
</div>
<div
v-if="productAndService && productAndService.length > 0"
class="row q-col-gutter-lg flex"
style="padding: 20px"
>
<div
:class="`${$q.screen.gt.sm ? 'col-3' : $q.screen.gt.xs ? 'col-6' : 'col-12'}`"
v-for="i in productAndServiceTab === 'all'
? productAndService
: productAndServiceTab === 'product'
? product
: service"
:key="i.id"
>
<TotalProductCardComponent
:data="i"
:key="i.id"
:title="i.name"
:isDisabled="i.status === 'INACTIVE' ? true : false"
@toggleStatus="
() => {
if (i.type === 'product') {
toggleStatusProduct(i.id, i.status);
}
if (i.type === 'service') {
toggleStatusService(i.id, i.status);
}
}
"
@menuViewDetail="
async () => {
if (i.type === 'product') {
currentIdProduct = i.id;
assignFormDataProduct(i);
dialogProductEdit = true;
}
if (i.type === 'service') {
currentIdService = i.id;
infoServiceEdit = false;
assignFormDataProductService(i.id);
dialogServiceEdit = true;
}
await fetchListOfOptionBranch();
}
"
@menuEdit="
async () => {
if (i.type === 'product') {
currentIdProduct = i.id;
infoProductEdit = true;
assignFormDataProduct(i);
dialogProductEdit = true;
}
if (i.type === 'service') {
currentIdService = i.id;
infoServiceEdit = true;
assignFormDataProductService(i.id);
dialogServiceEdit = true;
}
await fetchListOfOptionBranch();
}
"
/>
</div>
</div>
<div class="row justify-between q-pb-md q-px-lg">
<div class="col-4">
<div class="row items-center">
<div class="app-text-muted" style="width: 80px">
{{ $t('showing') }}
</div>
<div>
<q-btn-dropdown
dense
unelevated
:label="pageSizeServiceAndProduct"
>
<q-list>
<q-item
v-for="v in [10, 30, 50, 100, 500, 1000]"
2024-07-05 02:42:09 +00:00
:key="v"
clickable
v-close-popup
@click="
async () => {
pageSizeServiceAndProduct = v;
await fetchListOfProductAndService();
}
"
>
<q-item-section>
<q-item-label>{{ v }}</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div>
</div>
<div class="col-4 row justify-center app-text-muted">
{{
$t('recordsPage', {
resultcurrentPage:
productAndServiceTab === 'all'
? productAndService.length
: productAndServiceTab === 'product'
? product?.length
: service?.length,
total: total,
})
}}
</div>
<div class="col-4 row justify-end">
<PaginationComponent
v-model:current-page="currentPageServiceAndProduct"
v-model:max-page="maxPageServiceAndProduct"
:fetch-data="
async () => {
await alternativeFetch();
flowStore.rotate();
}
"
/>
</div>
</div>
2024-07-02 09:58:59 +00:00
</div>
2024-06-12 15:37:17 +07:00
</div>
2024-06-10 16:42:59 +07:00
</div>
<FormDialog
v-model:modal="dialogInputForm"
noAddress
2024-06-12 15:37:17 +07:00
:title="$t('addProduct')"
:submit="() => (productMode === 'type' ? submitType() : submitGroup())"
:close="clearFormGroup"
>
<template #information>
<BasicInformation
dense
2024-06-11 16:52:35 +07:00
:isType="productMode === 'type'"
v-model:remark="formDataGroup.remark"
v-model:name="formDataGroup.name"
v-model:detail="formDataGroup.detail"
/>
</template>
</FormDialog>
<DrawerInfo
:show-edit="!currentStatusProduct"
ref="formDialogRef"
v-model:drawerOpen="drawerInfo"
2024-06-28 14:14:23 +07:00
:title="
$t(productMode === 'group' ? 'productGroup' : 'productType', {
name: formDataGroup.code,
})
"
:undo="() => undoProductGroup()"
:isEdit="isEdit"
:editData="() => (isEdit = true)"
2024-06-11 16:52:35 +07:00
:submit="() => (productMode === 'type' ? submitType() : submitGroup())"
:delete-data="() => deleteProductById()"
:close="() => (drawerInfo = false)"
>
<template #info>
2024-06-27 04:55:39 +00:00
<InfoForm noAddress>
<template #information>
<!-- <AppBox class="q-ma-md" bordered> -->
<BasicInformation
dense
:isType="productMode === 'type'"
:readonly="!isEdit"
v-model:remark="formDataGroup.remark"
v-model:name="formDataGroup.name"
v-model:code="formDataGroup.code"
v-model:detail="formDataGroup.detail"
/>
<!-- </AppBox> -->
</template>
</InfoForm>
</template>
</DrawerInfo>
<FormDialog
v-model:modal="dialogProductServiceType"
:title="'สร้างสินค้าและบริการ'"
no-footer
no-app-box
2024-06-18 11:47:53 +00:00
max-width="80%"
:close="() => (dialogProductServiceType = false)"
>
<template #body>
<div class="row q-gutter-xl q-pa-sm">
<ItemCard
class="col"
v-for="i in itemCard"
changeColor
:key="i.text"
:icon="i.icon"
:text="i.text"
:color="i.color"
@trigger="
() => {
dialogProductServiceType = false;
}
"
/>
</div>
</template>
</FormDialog>
<FormDialog
v-model:modal="dialogTotalProduct"
noAddress
noAppBox
2024-06-19 18:00:38 +07:00
title="สินค้าทั้งหมด"
:submit="
() => {
2024-06-20 18:02:02 +07:00
selectProduct.forEach((i) => {
workItems[currentWorkIndex].product.push({
...i,
nameEn: '',
});
});
dialogTotalProduct = false;
selectProduct = [];
}
"
:close="
() => {
dialogTotalProduct = false;
inputSearchProductAndService = '';
}
"
>
<div class="full-width full-height">
<div class="row items-center">
2024-06-19 18:00:38 +07:00
<q-space />
<q-input
outlined
dense
:label="$t('search')"
:bg-color="$q.dark.isActive ? 'dark' : 'white'"
v-model="inputSearchProductAndService"
2024-06-19 18:00:38 +07:00
debounce="500"
@update:model-value="searchProduct()"
2024-06-19 18:00:38 +07:00
>
<template v-slot:prepend>
<q-icon name="mdi-magnify" />
</template>
</q-input>
2024-06-13 15:59:09 +07:00
</div>
<div>
<AppBox class="row" style="background: none">
<div
class="col-12 text-center"
v-if="resultSearchProduct?.length === 0"
>
<NoData />
</div>
<div
class="q-pa-xs"
:class="`${$q.screen.gt.sm ? 'col-3 ' : $q.screen.gt.xs ? 'col-6' : 'col-12'}`"
v-for="i in (!!inputSearchProductAndService
? resultSearchProduct
: productIsAdd
)?.filter((i) => {
if (i.status === 'INACTIVE') {
return false;
}
return true;
})"
:key="i.id"
>
<TotalProductCardComponent
:index="selectProduct.findIndex((v) => v.id === i.id)"
:isAddProduct="!!selectProduct.find((v) => v.id === i.id)"
:isSelected="true"
:data="{ ...i, type: 'product' }"
:title="i.name"
:status="i.status === 'INACTIVE' ? true : false"
@menuViewDetail="
() => {
currentIdProduct = i.id;
assignFormDataProduct(i);
dialogProductEdit = true;
}
"
@menuEdit="
() => {
currentIdProduct = i.id;
infoProductEdit = true;
assignFormDataProduct(i);
dialogProductEdit = true;
}
"
@viewDetail="
() => {
currentIdProduct = i.id;
infoProductEdit = false;
assignFormDataProduct(i);
dialogProductEdit = true;
}
"
@select="
(data) => {
const tempValue = selectProduct.find((v) => v.id === i.id);
if (tempValue) {
selectProduct = selectProduct.filter((v) => v.id !== i.id);
} else {
selectProduct.push(data);
}
}
"
/>
</div>
</AppBox>
</div>
2024-06-19 18:00:38 +07:00
</div>
</FormDialog>
2024-06-14 14:45:17 +07:00
<!-- Add Product -->
2024-06-14 14:45:17 +07:00
<FormDialog
v-model:modal="dialogProduct"
noAddress
:title="$t('buttonAddProduct')"
2024-06-14 14:45:17 +07:00
:submit="
() => {
submitProduct();
}
"
:close="
() => {
dialogProduct = false;
2024-06-26 17:21:26 +07:00
flowStore.rotate();
}
"
>
<template #prepend>
<ProfileUpload
isProduct
v-model:url-profile="profileUrl"
v-model:status-toggle="statusToggle"
v-model:profile-submit="profileSubmit"
@input-file="inputFile.click()"
/>
</template>
2024-06-21 08:49:20 +00:00
<template #information>
<BasicInfoProduct
:options-branch="branchOption"
v-model:registered-branch-id="formDataProduct.registeredBranchId"
v-model:detail="formDataProduct.detail"
v-model:remark="formDataProduct.remark"
v-model:name="formDataProduct.name"
v-model:code="formDataProduct.code"
2024-06-18 16:35:40 +07:00
v-model:process="formDataProduct.process"
dense
separator
/>
2024-06-21 08:49:20 +00:00
</template>
<template #person>
<PriceDataComponent
v-model:price="formDataProduct.price"
v-model:agent-price="formDataProduct.agentPrice"
v-model:service-charge="formDataProduct.serviceCharge"
dense
/>
2024-06-21 08:49:20 +00:00
</template>
</FormDialog>
<!-- edit product -->
<FormDialog
:edit="!(formDataProduct.status === 'INACTIVE')"
:isEdit="infoProductEdit"
v-model:modal="dialogProductEdit"
noAddress
:title="$t('editProduct')"
:editData="() => (infoProductEdit = true)"
:undo="
() => {
formDataProduct = { ...prevProduct };
infoProductEdit = false;
2024-06-26 17:21:26 +07:00
flowStore.rotate();
}
"
:deleteData="
() => {
deleteTypeOfProduct();
}
"
:submit="
() => {
submitProduct();
2024-06-14 14:45:17 +07:00
}
"
:close="
() => {
dialogProductEdit = false;
2024-06-26 17:21:26 +07:00
flowStore.rotate();
2024-06-14 14:45:17 +07:00
}
"
>
<template #prepend>
<ProfileUpload
isProduct
v-model:url-profile="profileUrl"
v-model:status-toggle="statusToggle"
v-model:profile-submit="profileSubmit"
@input-file="inputFile.click()"
:label-status-toggle="'formDialogTitleProductStatus'"
2024-06-14 14:45:17 +07:00
/>
</template>
2024-06-21 08:49:20 +00:00
<template #information>
<BasicInfoProduct
:options-branch="branchOption"
:readonly="!infoProductEdit"
v-model:registered-branch-id="formDataProduct.registeredBranchId"
v-model:detail="formDataProduct.detail"
v-model:remark="formDataProduct.remark"
v-model:name="formDataProduct.name"
v-model:code="formDataProduct.code"
v-model:process="formDataProduct.process"
dense
separator
/>
2024-06-21 08:49:20 +00:00
</template>
<template #person>
<PriceDataComponent
:readonly="!infoProductEdit"
v-model:price="formDataProduct.price"
v-model:agent-price="formDataProduct.agentPrice"
v-model:service-charge="formDataProduct.serviceCharge"
dense
/>
2024-06-21 08:49:20 +00:00
</template>
2024-06-14 14:45:17 +07:00
</FormDialog>
2024-06-18 02:01:02 +00:00
2024-06-18 11:47:53 +00:00
<!-- add service -->
2024-06-18 02:01:02 +00:00
<FormDialog
no-address
2024-06-25 09:35:06 +00:00
no-app-box
2024-06-21 08:49:20 +00:00
height="95vh"
2024-06-18 02:01:02 +00:00
:title="$t('addService')"
v-model:modal="dialogService"
2024-06-28 02:21:21 +00:00
:tabs-list="serviceTab"
2024-06-18 02:01:02 +00:00
v-model:current-tab="currentServiceTab"
2024-06-21 11:40:18 +07:00
:submit="
() => {
submitService();
}
"
:close="
() => {
clearFormService();
2024-06-21 11:40:18 +07:00
dialogService = false;
2024-06-26 17:21:26 +07:00
flowStore.rotate();
2024-06-21 11:40:18 +07:00
}
"
2024-06-18 02:01:02 +00:00
>
<template #prepend>
<ProfileUpload
2024-06-18 02:28:29 +00:00
isService
2024-06-18 02:01:02 +00:00
v-model:url-profile="profileUrl"
v-model:status-toggle="statusToggle"
v-model:profile-submit="profileSubmit"
@input-file="inputFile.click()"
/>
</template>
2024-07-01 08:40:37 +00:00
<div
v-if="currentServiceTab === 'serviceInformation'"
class="col-md-10 col-sm-12"
>
2024-06-25 09:35:06 +00:00
<div class="surface-1 rounded bordered q-pa-lg full-width row">
<BasicInformation
dense
service
:options-branch="branchOption"
v-model:registered-branch-id="
formDataProductService.registeredBranchId
"
2024-06-25 09:35:06 +00:00
v-model:service-code="formDataProductService.code"
v-model:service-description="formDataProductService.detail"
v-model:service-name-th="formDataProductService.name"
/>
</div>
<div class="surface-1 rounded bordered q-mt-md q-pa-lg row">
<FormServiceProperties
v-model:service-attributes="formDataProductService.attributes"
@service-properties="
() => {
tempValueProperties = formDataProductService.attributes;
openPropertiesDialog('service');
}
"
/>
</div>
</div>
<div
v-if="currentServiceTab === 'workInformation'"
2024-07-01 08:40:37 +00:00
class="surface-1 rounded bordered col-md-10 col-sm-12 q-pa-lg"
2024-06-25 09:35:06 +00:00
>
2024-06-18 02:01:02 +00:00
<FormServiceWork
2024-06-20 18:02:02 +07:00
v-model:work-items="workItems"
2024-06-18 02:01:02 +00:00
dense
2024-06-20 18:02:02 +07:00
@addProduct="
async (index) => {
await fetchListOfProductIsAdd(currentIdType);
2024-06-20 18:02:02 +07:00
currentWorkIndex = index;
dialogTotalProduct = true;
}
"
@manage-work-name="
() => {
manageWorkNameDialog = true;
}
"
2024-06-20 18:02:02 +07:00
@work-properties="
(index) => {
currentWorkIndex = index;
tempValueProperties = workItems[index].attributes;
openPropertiesDialog('work');
}
"
2024-06-18 02:01:02 +00:00
/>
2024-06-25 09:35:06 +00:00
</div>
2024-06-18 02:01:02 +00:00
</FormDialog>
2024-06-18 11:47:53 +00:00
<!-- service properties -->
<FormDialog
no-address
no-app-box
height="75vh"
2024-06-18 11:47:53 +00:00
width="75%"
2024-06-24 04:45:58 +00:00
:title="$t('properties')"
2024-06-18 11:47:53 +00:00
v-model:modal="propertiesDialog"
2024-06-20 18:02:02 +07:00
:submit="
() => {
if (currentPropertiesMode === 'service') {
formDataProductService.attributes = tempValueProperties;
}
if (currentPropertiesMode === 'work') {
workItems[currentWorkIndex].attributes = tempValueProperties;
}
propertiesDialog = false;
}
"
:close="
() => {
propertiesDialog = false;
}
"
2024-06-18 11:47:53 +00:00
>
<ServiceProperties
v-model:properties-option="propertiesOption"
2024-06-20 18:02:02 +07:00
v-model:form-service-properties="tempValueProperties"
2024-06-18 11:47:53 +00:00
/>
</FormDialog>
<!-- manage work name -->
<FormDialog
no-address
no-app-box
2024-06-21 11:06:35 +00:00
no-footer
height="65vh"
width="65%"
v-model:modal="manageWorkNameDialog"
:title="$t('manage')"
:close="triggerConfirmCloseWork"
>
2024-06-21 11:06:35 +00:00
<WorkNameManagement
ref="workNameRef"
v-model:nameList="workNameItems"
@delete="confirmDeleteWork"
@edit="editWork"
@add="createWork"
/>
</FormDialog>
<!-- edit service -->
<FormDialog
no-address
2024-06-25 11:04:34 +00:00
:no-app-box="currentServiceTab !== 'workInformation'"
2024-06-24 16:53:33 +07:00
:edit="!(formDataProductService.status === 'INACTIVE')"
2024-06-24 10:13:23 +00:00
height="95vh"
:isEdit="infoServiceEdit"
:title="$t('service', { msg: formDataProductService.name })"
2024-06-28 02:21:21 +00:00
:tabs-list="serviceTab"
v-model:modal="dialogServiceEdit"
v-model:current-tab="currentServiceTab"
:submit="
() => {
submitService();
}
"
:close="
() => {
clearFormService();
2024-06-26 17:21:26 +07:00
flowStore.rotate();
dialogServiceEdit = false;
}
"
:edit-data="
() => {
infoServiceEdit = true;
}
"
2024-06-24 16:53:33 +07:00
:undo="
() => {
infoServiceEdit = false;
2024-06-26 17:44:13 +07:00
cloneData();
2024-06-24 16:53:33 +07:00
statusToggle = prevService.status === 'INACTIVE' ? false : true;
2024-06-26 17:21:26 +07:00
flowStore.rotate();
2024-06-24 16:53:33 +07:00
}
"
:delete-data="() => deleteServiceById()"
>
<template #prepend>
<ProfileUpload
isService
2024-07-01 11:04:24 +00:00
:readonly="!infoServiceEdit"
v-model:url-profile="profileUrl"
v-model:status-toggle="statusToggle"
v-model:profile-submit="profileSubmit"
@input-file="inputFile.click()"
/>
</template>
2024-06-25 11:04:34 +00:00
<div
v-if="currentServiceTab === 'serviceInformation'"
class="col-md-10 col-sm-12"
>
2024-06-25 09:35:06 +00:00
<div class="surface-1 rounded bordered q-pa-lg full-width row">
<BasicInformation
:readonly="!infoServiceEdit"
v-if="currentServiceTab === 'serviceInformation'"
dense
service
:options-branch="branchOption"
v-model:registered-branch-id="
formDataProductService.registeredBranchId
"
2024-06-25 09:35:06 +00:00
v-model:service-code="formDataProductService.code"
v-model:service-description="formDataProductService.detail"
v-model:service-name-th="formDataProductService.name"
/>
</div>
<div class="surface-1 rounded bordered q-mt-md q-pa-lg row">
<FormServiceProperties
:readonly="!infoServiceEdit"
v-model:service-attributes="formDataProductService.attributes"
@service-properties="
() => {
tempValueProperties = formDataProductService.attributes;
openPropertiesDialog('service');
}
"
/>
</div>
</div>
2024-06-25 11:04:34 +00:00
<template #information>
<FormServiceWork
:readonly="!infoServiceEdit"
v-model:work-items="workItems"
v-if="currentServiceTab === 'workInformation'"
dense
@addProduct="
async (index) => {
await fetchListOfProduct();
currentWorkIndex = index;
dialogTotalProduct = true;
}
"
@manage-work-name="manageWorkNameDialog = true"
@work-properties="
(index) => {
currentWorkIndex = index;
tempValueProperties = workItems[index].attributes;
openPropertiesDialog('work');
}
"
/>
2024-06-25 11:04:34 +00:00
</template>
</FormDialog>
2024-06-10 16:42:59 +07:00
</template>
<style scoped>
.hover-underline:hover {
cursor: pointer;
text-decoration: underline;
}
</style>