refactor: add grid / table view to product service

This commit is contained in:
Methapon2001 2024-07-09 11:19:10 +07:00
parent dbb366ead3
commit face6973cd

View file

@ -1,8 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue'; import { ref, watch, reactive } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import type { QTableProps } from 'quasar';
import ItemCard from 'components/ItemCard.vue'; import ItemCard from 'components/ItemCard.vue';
import AppBox from 'components/app/AppBox.vue'; import AppBox from 'components/app/AppBox.vue';
import AddButton from 'components/AddButton.vue'; import AddButton from 'components/AddButton.vue';
@ -120,6 +121,8 @@ const currentStatusProduct = ref(false);
const drawerInfo = ref(false); const drawerInfo = ref(false);
const isEdit = ref(false); const isEdit = ref(false);
const modeView = ref(false);
const dialogInputForm = ref(false); const dialogInputForm = ref(false);
const dialogProduct = ref(false); const dialogProduct = ref(false);
const dialogService = ref(false); const dialogService = ref(false);
@ -149,7 +152,7 @@ const service = ref<(Service & { type: 'service' })[]>();
const resultSearchProduct = ref<ProductList[]>(); const resultSearchProduct = ref<ProductList[]>();
const productAndService = ref<ServiceAndProduct[]>([]); const productAndService = ref<ServiceAndProduct[]>([]);
const productAndServiceTab = ref<'all' | 'product' | 'service'>('all'); const productAndServiceTab = ref<'product' | 'service'>('service');
const manageWorkNameDialog = ref(false); const manageWorkNameDialog = ref(false);
const previousValue = ref(); const previousValue = ref();
@ -200,6 +203,69 @@ const serviceTab = [
]; ];
const hideStat = ref(false); const hideStat = ref(false);
const tbColumn = {
product: [
{ name: 'productName', align: 'left', label: 'productName', field: 'name' },
{
name: 'productDetail',
align: 'left',
label: 'productDetail',
field: 'detail',
},
{
name: 'productProcessingTime',
align: 'left',
label: 'productProcessingTime',
field: 'process',
},
{
name: 'priceInformation',
align: 'left',
label: 'priceInformation',
field: 'name',
},
],
service: [
{ name: 'serviceName', align: 'left', label: 'serviceName', field: 'name' },
{
name: 'serviceDetail',
align: 'left',
label: 'serviceDetail',
field: 'detail',
},
{
name: 'serviceWorkTotal',
align: 'left',
label: 'serviceWorkTotal',
field: (v) => v.work.length,
},
],
} satisfies {
product: QTableProps['columns'];
service: QTableProps['columns'];
};
const tbControl = reactive({
product: {
fieldDisplay: [
'productName',
'productDetail',
'productProcessingTime',
'priceInformation',
],
fieldSelected: [
'productName',
'productDetail',
'productProcessingTime',
'priceInformation',
],
},
service: {
fieldDisplay: ['serviceName', 'serviceDetail', 'serviceWorkTotal'],
fieldSelected: ['serviceName', 'serviceDetail', 'serviceWorkTotal'],
},
});
const workItems = ref<WorkItems[]>([]); const workItems = ref<WorkItems[]>([]);
const workNameRef = ref(); const workNameRef = ref();
const selectProduct = ref<ProductList[]>([]); const selectProduct = ref<ProductList[]>([]);
@ -541,10 +607,7 @@ async function deleteServiceById(serviceId?: string) {
const res = await deleteService(serviceId ?? currentIdService.value); const res = await deleteService(serviceId ?? currentIdService.value);
if (productAndServiceTab.value === 'service') { if (productAndServiceTab.value === 'service') {
await await fetchListOfService(); await fetchListOfService();
}
if (productAndServiceTab.value === 'all') {
await fetchListOfProductAndService();
} }
dialogServiceEdit.value = false; dialogServiceEdit.value = false;
@ -573,9 +636,6 @@ async function deleteTypeOfProduct(id?: string) {
if (productAndServiceTab.value === 'product') { if (productAndServiceTab.value === 'product') {
await fetchListOfProduct(); await fetchListOfProduct();
} }
if (productAndServiceTab.value === 'all') {
await fetchListOfProductAndService();
}
dialogProductEdit.value = false; dialogProductEdit.value = false;
@ -722,7 +782,6 @@ const prevProduct = ref<ProductCreate>({
function assignFormDataProduct(data: ProductList) { function assignFormDataProduct(data: ProductList) {
statusToggle.value = data.status === 'INACTIVE' ? false : true; statusToggle.value = data.status === 'INACTIVE' ? false : true;
console.log(data);
profileUrl.value = `${baseUrl.value}/product/${data?.id}/image`; profileUrl.value = `${baseUrl.value}/product/${data?.id}/image`;
profileSubmit.value = true; profileSubmit.value = true;
@ -835,9 +894,6 @@ async function submitService() {
}); });
} }
if (productAndServiceTab.value === 'all') {
await fetchListOfProductAndService();
}
if (productAndServiceTab.value === 'service') { if (productAndServiceTab.value === 'service') {
await fetchListOfService(); await fetchListOfService();
} }
@ -868,9 +924,6 @@ async function submitProduct() {
totalProduct.value = totalProduct.value + 1; totalProduct.value = totalProduct.value + 1;
clearFormProduct(); clearFormProduct();
if (productAndServiceTab.value === 'all') {
await fetchListOfProductAndService();
}
if (productAndServiceTab.value === 'product') { if (productAndServiceTab.value === 'product') {
await fetchListOfProduct(); await fetchListOfProduct();
} }
@ -957,10 +1010,6 @@ async function calculateStats() {
} }
async function fetchStatus() { async function fetchStatus() {
if (productAndServiceTab.value === 'all') {
await fetchListOfProductAndService();
flowStore.rotate();
}
if (productAndServiceTab.value === 'product') { if (productAndServiceTab.value === 'product') {
await fetchListOfProduct(); await fetchListOfProduct();
flowStore.rotate(); flowStore.rotate();
@ -973,15 +1022,6 @@ async function fetchStatus() {
} }
async function alternativeFetch() { async function alternativeFetch() {
console.log('asdasds');
if (productAndServiceTab.value === 'all') {
await fetchListOfProductAndService();
flowStore.rotate();
}
if (productAndServiceTab.value === 'product') { if (productAndServiceTab.value === 'product') {
await fetchListOfProduct(); await fetchListOfProduct();
@ -1163,10 +1203,7 @@ watch(inputSearchProductAndService, async () => {
</div> </div>
<div v-if="stat[0].count !== 0" class="column full-height no-wrap"> <div v-if="stat[0].count !== 0" class="column full-height no-wrap">
<div <div class="text-body-2 q-mb-xs flex items-center">
v-if="productMode === 'group' || productMode === 'type'"
class="text-body-2 q-mb-xs flex items-center"
>
{{ $t('dataSum') }} {{ $t('dataSum') }}
<q-btn <q-btn
class="q-ml-xs" class="q-ml-xs"
@ -1182,17 +1219,8 @@ watch(inputSearchProductAndService, async () => {
/> />
</div> </div>
<transition <transition name="slide">
v-if="productMode === 'group' || productMode === 'type'" <div v-if="!hideStat" class="scroll q-mb-md">
name="slide"
>
<div
v-if="
(productMode === 'group' && !hideStat) ||
(productMode === 'type' && !hideStat)
"
class="scroll q-mb-md"
>
<div style="display: inline-block"> <div style="display: inline-block">
<StatCard <StatCard
label-i18n label-i18n
@ -1342,7 +1370,7 @@ watch(inputSearchProductAndService, async () => {
pathTypeName = v.name; pathTypeName = v.name;
currentIdType = v.id; currentIdType = v.id;
productMode = 'service'; productMode = 'service';
productAndServiceTab = 'all'; productAndServiceTab = 'service';
// await featchStatsService(); // await featchStatsService();
// await featchStatsProduct(); // await featchStatsProduct();
await fetchListOfProduct(); await fetchListOfProduct();
@ -1542,7 +1570,7 @@ watch(inputSearchProductAndService, async () => {
class="surface-2 col bordered rounded column" class="surface-2 col bordered rounded column"
> >
<div <div
class="row justify-between items-center surface-1 q-px-md q-py-sm rounded surface-3 bordered" class="row justify-between items-center q-px-md q-py-sm rounded surface-2"
style="z-index: 1" style="z-index: 1"
> >
<div> <div>
@ -1562,7 +1590,7 @@ watch(inputSearchProductAndService, async () => {
</template> </template>
</q-input> </q-input>
</div> </div>
<div class="row"> <div class="row" style="gap: var(--size-2)">
<q-btn <q-btn
icon="mdi-tune-vertical-variant" icon="mdi-tune-vertical-variant"
size="sm" size="sm"
@ -1612,13 +1640,56 @@ watch(inputSearchProductAndService, async () => {
</q-list> </q-list>
</q-menu> </q-menu>
</q-btn> </q-btn>
<q-btn-toggle
v-model="modeView"
id="btn-mode"
dense
class="no-shadow bordered rounded surface-1"
:toggle-color="$q.dark.isActive ? 'grey-9' : 'grey-2'"
size="xs"
:options="[
{ value: true, slot: 'folder' },
{ value: false, slot: 'list' },
]"
>
<template v-slot:folder>
<q-icon
name="mdi-view-grid-outline"
size="16px"
class="q-px-sm q-py-xs rounded"
:style="{
color: $q.dark.isActive
? modeView
? '#C9D3DB '
: '#787B7C'
: modeView
? '#787B7C'
: '#C9D3DB',
}"
/>
</template>
<template v-slot:list>
<q-icon
name="mdi-format-list-bulleted"
class="q-px-sm q-py-xs rounded"
size="16px"
:style="{
color: $q.dark.isActive
? modeView === false
? '#C9D3DB'
: '#787B7C'
: modeView === false
? '#787B7C'
: '#C9D3DB',
}"
/>
</template>
</q-btn-toggle>
</div> </div>
</div> </div>
<div <div
v-if=" v-if="
(productAndServiceTab === 'all' &&
productAndService?.length === 0) ||
(productAndServiceTab === 'product' && product?.length === 0) || (productAndServiceTab === 'product' && product?.length === 0) ||
(productAndServiceTab === 'service' && service?.length === 0) (productAndServiceTab === 'service' && service?.length === 0)
" "
@ -1632,63 +1703,14 @@ watch(inputSearchProductAndService, async () => {
<div <div
v-if="productAndService && productAndService.length > 0" v-if="productAndService && productAndService.length > 0"
class="row flex col q-px-md" class="row flex col"
> >
<q-tabs <q-tabs
dense dense
v-model="productAndServiceTab" v-model="productAndServiceTab"
class="col-12 q-mb-lg bordered-b" class="col-12 bordered-b q-px-md"
align="left" align="left"
style="border-top-left-radius: var(--radius-2)"
> >
<q-tab
name="all"
@click="
async () => {
currentPageServiceAndProduct = 1;
inputSearchProductAndService = '';
currentStatus = 'All';
pageSizeServiceAndProduct;
// await fetchListOfProductAndService();
flowStore.rotate();
}
"
>
<div
class="row"
:class="
productAndServiceTab === 'all'
? 'text-bold'
: 'app-text-muted'
"
>
{{ $t('all') }}
</div>
</q-tab>
<q-tab
name="product"
@click="
async () => {
currentPageServiceAndProduct = 1;
inputSearchProductAndService = '';
currentStatus = 'All';
// await fetchListOfProduct();
flowStore.rotate();
}
"
>
<div
class="row"
:class="
productAndServiceTab === 'product'
? 'text-bold'
: 'app-text-muted'
"
>
{{ $t('product') }}
</div>
</q-tab>
<q-tab <q-tab
name="service" name="service"
@click=" @click="
@ -1712,67 +1734,394 @@ watch(inputSearchProductAndService, async () => {
{{ $t('service') }} {{ $t('service') }}
</div> </div>
</q-tab> </q-tab>
</q-tabs> <q-tab
<div class="col-12 row flex col q-col-gutter-sm scroll full-height"> name="product"
<div @click="
class="col-md-3 col-sm-6 col-12" async () => {
v-for="i in productAndServiceTab === 'all' currentPageServiceAndProduct = 1;
? productAndService inputSearchProductAndService = '';
: productAndServiceTab === 'product' currentStatus = 'All';
? product // await fetchListOfProduct();
: service" flowStore.rotate();
:key="i.id" }
"
> >
<TotalProductCardComponent <div
:data="i" class="row"
:key="i.id" :class="
:title="i.name" productAndServiceTab === 'product'
:isDisabled="i.status === 'INACTIVE' ? true : false" ? 'text-bold'
@toggleStatus=" : 'app-text-muted'
() => {
if (i.type === 'product') {
toggleStatusProduct(i.id, i.status);
}
if (i.type === 'service') {
toggleStatusService(i.id, i.status);
}
}
" "
@menuViewDetail=" >
async () => { {{ $t('product') }}
if (i.type === 'product') { </div>
currentIdProduct = i.id; </q-tab>
assignFormDataProduct(i); </q-tabs>
dialogProductEdit = true; <div class="col-12 flex col scroll full-height q-pa-md surface-1">
} <div class="full-width">
if (i.type === 'service') { <q-table
currentIdService = i.id; class="full-width"
infoServiceEdit = false; flat
assignFormDataProductService(i.id); bordered
dialogServiceEdit = true; :rows-per-page-options="[0]"
} :rows="{ product, service }[productAndServiceTab] || []"
await fetchListOfOptionBranch(); :columns="
} {
product: tbColumn.product,
service: tbColumn.service,
}[productAndServiceTab]
" "
@menuEdit=" :grid="modeView"
async () => { card-container-class="row q-col-gutter-md"
if (i.type === 'product') { row-key="name"
currentIdProduct = i.id; hide-pagination
infoProductEdit = true; :visible-columns="
assignFormDataProduct(i); {
dialogProductEdit = true; product: tbControl.product.fieldSelected,
} service: tbControl.service.fieldSelected,
if (i.type === 'service') { }[productAndServiceTab]
currentIdService = i.id; "
infoServiceEdit = true; >
assignFormDataProductService(i.id); <template v-slot:header="props">
dialogServiceEdit = true; <q-tr class="surface-2" :props="props">
} <q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
>
{{ $t(col.label) }}
</q-th>
<q-th auto-width />
</q-tr>
</template>
await fetchListOfOptionBranch(); <template v-slot:body="props">
} <q-tr
" :class="{
/> 'app-text-muted': props.row.status === 'INACTIVE',
}"
:props="props"
>
<q-td
v-if="
{
product: tbControl.product.fieldSelected,
service: tbControl.service.fieldSelected,
}[productAndServiceTab].includes(
{ product: 'productName', service: 'serviceName' }[
productAndServiceTab
],
)
"
>
<div class="row items-center">
<div
:class="{
'status-active': props.row.status !== 'INACTIVE',
'status-inactive': props.row.status === 'INACTIVE',
}"
style="
width: 50px;
display: flex;
margin-bottom: var(--size-2);
"
>
<div class="table__icon">
<q-icon
size="md"
style="scale: 0.8"
name="mdi-office-building-outline"
/>
</div>
</div>
<div class="col">
<div class="col">{{ props.row.name }}</div>
<div class="col app-text-muted">
{{ props.row.code }}
</div>
</div>
</div>
</q-td>
<q-td
v-if="
{
product: tbControl.product.fieldSelected,
service: tbControl.service.fieldSelected,
}[productAndServiceTab].includes(
{
product: 'productDetail',
service: 'serviceDetail',
}[productAndServiceTab],
)
"
>
{{ props.row.detail }}
</q-td>
<q-td
v-if="
{
product: tbControl.product.fieldSelected,
service: tbControl.service.fieldSelected,
}[productAndServiceTab].includes(
{
product: 'productProcessingTime',
service: 'serviceWorkTotal',
}[productAndServiceTab],
)
"
>
{{
{
product: props.row.remark,
service: props.row.work?.length,
}[productAndServiceTab]
}}
</q-td>
<q-td
v-if="
{
product: tbControl.product.fieldSelected,
service: tbControl.service.fieldSelected,
}[productAndServiceTab].includes(
{
product: 'productProcessingTime',
service: 'empty',
}[productAndServiceTab],
)
"
>
{{ props.row.process }}
</q-td>
<q-td>
<q-btn
icon="mdi-eye-outline"
size="sm"
dense
round
flat
@click.stop="
async () => {
if (props.row.type === 'product') {
currentIdProduct = props.row.id;
assignFormDataProduct(props.row);
dialogProductEdit = true;
}
if (props.row.type === 'service') {
currentIdService = props.row.id;
infoServiceEdit = false;
assignFormDataProductService(props.row.id);
dialogServiceEdit = true;
}
await fetchListOfOptionBranch();
}
"
/>
<q-btn
icon="mdi-dots-vertical"
size="sm"
dense
round
flat
@click.stop
>
<q-menu class="bordered">
<q-list v-close-popup>
<q-item
:id="`view-detail-btn-${props.row.name}-view`"
@click.stop="
async () => {
if (props.row.type === 'product') {
currentIdProduct = props.row.id;
assignFormDataProduct(props.row);
dialogProductEdit = true;
}
if (props.row.type === 'service') {
currentIdService = props.row.id;
infoServiceEdit = false;
assignFormDataProductService(props.row.id);
dialogServiceEdit = true;
}
await fetchListOfOptionBranch();
}
"
clickable
dense
class="row q-py-sm"
style="white-space: nowrap"
>
<q-icon
name="mdi-eye-outline"
class="col-3"
size="xs"
style="color: hsl(var(--green-6-hsl))"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('viewDetail') }}
</span>
</q-item>
<q-item
:id="`view-detail-btn-${props.row.name}-edit`"
clickable
dense
class="row q-py-sm"
style="white-space: nowrap"
@click="
async () => {
if (props.row.type === 'product') {
currentIdProduct = props.row.id;
infoProductEdit = true;
assignFormDataProduct(props.row);
dialogProductEdit = true;
}
if (props.row.type === 'service') {
currentIdService = props.row.id;
infoServiceEdit = true;
assignFormDataProductService(props.row.id);
dialogServiceEdit = true;
}
await fetchListOfOptionBranch();
}
"
>
<q-icon
name="mdi-pencil-outline"
class="col-3"
size="xs"
style="color: hsl(var(--cyan-6-hsl))"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('edit') }}
</span>
</q-item>
<q-item
:id="`view-detail-btn-${props.row.name}-delete`"
dense
:clickable="props.row.status === 'CREATED'"
class="row"
:class="{
'surface-3': props.row.status !== 'CREATED',
'app-text-muted':
props.row.status !== 'CREATED',
}"
style="white-space: nowrap"
@click=""
>
<q-icon
name="mdi-trash-can-outline"
size="xs"
class="col-3"
:class="{
'app-text-negative':
props.row.status === 'CREATED',
}"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('delete') }}
</span>
</q-item>
<q-item dense>
<q-item-section class="q-py-sm">
<div class="q-pa-sm surface-2 rounded">
<q-toggle
:id="`view-detail-btn-${props.row.name}-status`"
dense
size="sm"
:label="
props.row.status !== 'INACTIVE'
? $t('switchOnLabel')
: $t('switchOffLabel')
"
@click="
() => {
if (props.row.type === 'product') {
toggleStatusProduct(
props.row.id,
props.row.status,
);
}
if (props.row.type === 'service') {
toggleStatusService(
props.row.id,
props.row.status,
);
}
}
"
:model-value="
props.row.status === 'CREATED' ||
props.row.status === 'ACTIVE'
"
/>
</div>
</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</q-td>
</q-tr>
</template>
<template v-slot:item="{ row }">
<div class="col-12 col-md-6">
<TotalProductCardComponent
:data="row"
:key="row.id"
:title="row.name"
:isDisabled="row.status === 'INACTIVE' ? true : false"
@toggleStatus="
() => {
if (row.type === 'product') {
toggleStatusProduct(row.id, row.status);
}
if (row.type === 'service') {
toggleStatusService(row.id, row.status);
}
}
"
@menuViewDetail="
async () => {
if (row.type === 'product') {
currentIdProduct = row.id;
assignFormDataProduct(row);
dialogProductEdit = true;
}
if (row.type === 'service') {
currentIdService = row.id;
infoServiceEdit = false;
assignFormDataProductService(row.id);
dialogServiceEdit = true;
}
await fetchListOfOptionBranch();
}
"
@menuEdit="
async () => {
if (row.type === 'product') {
currentIdProduct = row.id;
infoProductEdit = true;
assignFormDataProduct(row);
dialogProductEdit = true;
}
if (row.type === 'service') {
currentIdService = row.id;
infoServiceEdit = true;
assignFormDataProductService(row.id);
dialogServiceEdit = true;
}
await fetchListOfOptionBranch();
}
"
/>
</div>
</template>
</q-table>
</div> </div>
</div> </div>
</div> </div>
@ -1821,11 +2170,9 @@ watch(inputSearchProductAndService, async () => {
{{ {{
$t('recordsPage', { $t('recordsPage', {
resultcurrentPage: resultcurrentPage:
productAndServiceTab === 'all' productAndServiceTab === 'product'
? productAndService.length ? product?.length
: productAndServiceTab === 'product' : service?.length,
? product?.length
: service?.length,
total: total, total: total,
}) })
}} }}