fix: new 03 ui & layout

This commit is contained in:
puriphatt 2024-07-05 09:29:46 +00:00
parent bebee654a5
commit e1e7eb4719

View file

@ -81,6 +81,7 @@ const {
const flowStore = useFlowStore(); const flowStore = useFlowStore();
const employeeStore = useEmployeeStore(); const employeeStore = useEmployeeStore();
const optionStore = useOptionStore(); const optionStore = useOptionStore();
const currentTab = ref('employer');
const formData = ref<CustomerCreate>({ const formData = ref<CustomerCreate>({
status: 'CREATED', status: 'CREATED',
personName: '', personName: '',
@ -129,6 +130,10 @@ const fieldSelectedCustomer = ref<{ label: string; value: string }>({
value: 'all', value: 'all',
}); });
const fieldSelected = ref();
const fieldDisplay = ref();
const splitterModel = ref(15);
const modeView = ref(true);
const currentEmployee = ref<Employee | undefined>(); const currentEmployee = ref<Employee | undefined>();
const currentEmployeeCode = ref(''); const currentEmployeeCode = ref('');
const isEmployeeEdit = ref(false); const isEmployeeEdit = ref(false);
@ -1216,6 +1221,7 @@ watch(locale, () => {
watch(fieldSelectedCustomer, async () => { watch(fieldSelectedCustomer, async () => {
let resultList; let resultList;
console.log('asd');
if (fieldSelectedCustomer.value.value === 'all') { if (fieldSelectedCustomer.value.value === 'all') {
resultList = await fetchList({ includeBranch: true }); resultList = await fetchList({ includeBranch: true });
@ -1278,6 +1284,12 @@ watch(genderSelector, async () => {
watch(selectorLabel, async () => { watch(selectorLabel, async () => {
if (inputSearch.value) inputSearch.value = undefined; if (inputSearch.value) inputSearch.value = undefined;
if (selectorLabel.value === 'EMPLOYEE') {
await fetchListEmployee();
await fetchListStatsEmployeeGender();
} else {
await fetchListCustomer();
}
}); });
watch([inputSearch, currentStatus], async () => { watch([inputSearch, currentStatus], async () => {
@ -1331,10 +1343,25 @@ watch([inputSearch, currentStatus], async () => {
<div class="column full-height no-wrap"> <div class="column full-height no-wrap">
<div v-if="isMainPage" class="column full-height"> <div v-if="isMainPage" class="column full-height">
<div class="text-body-2 q-mb-xs flex items-center"> <div class="text-body-2 text-weight-medium q-mb-xs flex items-center">
{{ $t('dataSum') }} {{ $t('dataSum') }}
<q-badge
v-if="listCustomer"
rounded
class="q-ml-sm"
style="
background-color: hsla(var(--info-bg) / 0.15);
color: hsl(var(--info-bg));
"
>
{{
selectorLabel === 'EMPLOYER'
? listCustomer?.length
: listEmployee.length
}}
</q-badge>
<q-btn <q-btn
class="q-ml-xs" class="q-ml-lg"
icon="mdi-pin" icon="mdi-pin"
color="primary" color="primary"
size="sm" size="sm"
@ -1390,420 +1417,535 @@ watch([inputSearch, currentStatus], async () => {
</div> </div>
<!-- main --> <!-- main -->
<div class="surface-1 bordered rounded q-pa-md scroll col"> <div class="surface-1 bordered rounded col column">
<div class="row q-pb-md"> <!-- tabs -->
<div class="col"> <div class="column">
<q-select
id="select-userType"
for="select-userType"
v-if="selectorLabel === 'EMPLOYER'"
v-model="fieldSelectedCustomer"
style="width: 150px"
outlined
:options="fieldCustomer.map((v) => ({ label: $t(v), value: v }))"
:label="$t('customerCardUserType')"
dense
/>
<q-btn-toggle
v-else
v-model="genderSelector"
id="btn-mode"
class="no-shadow"
padding="0px"
toggle-color="grey-3"
size="xs"
:options="[
{ value: 'female', slot: 'female' },
{ value: 'male', slot: 'male' },
]"
>
<template v-slot:female>
<div
class="bordered-t bordered-l bordered-b q-px-md q-py-sm app-text-female text-weight-medium text-body2 row items-center"
@click="
async () => {
if (genderSelector === 'female') {
await fetchListEmployee();
genderSelector = '';
}
}
"
>
<q-icon
name="mdi-gender-female"
class="q-mr-sm"
size="18px"
/>
<div v-if="statsEmployeeGender">
{{ statsEmployeeGender.female ?? 0 }} {{ $t('person') }}
</div>
</div>
</template>
<template v-slot:male>
<div
class="bordered q-px-md q-py-sm app-text-male text-weight-medium text-body2 row items-center"
@click="
async () => {
if (genderSelector === 'male') {
await fetchListEmployee();
genderSelector = '';
}
}
"
>
<q-icon name="mdi-gender-male" class="q-mr-sm" size="18px" />
<div v-if="statsEmployeeGender">
{{ statsEmployeeGender.male ?? 0 }} {{ $t('person') }}
</div>
</div>
</template>
</q-btn-toggle>
</div>
<q-input
for="input-search"
outlined
dense
style="width: 250px"
:label="$t('search')"
class="q-mr-lg"
:bg-color="$q.dark.isActive ? 'dark' : 'white'"
v-model="inputSearch"
debounce="200"
/>
<q-btn
id="btn-status"
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
id="btn-status-all"
clickable
class="flex items-center"
@click="currentStatus = 'All'"
>
{{ $t('all') }}
</q-item>
<q-item
id="btn-status-active"
clickable
class="flex items-center"
@click="currentStatus = 'ACTIVE'"
>
{{ $t('statusACTIVE') }}
</q-item>
<q-item
id="btn-status-inactive"
clickable
class="flex items-center"
@click="currentStatus = 'INACTIVE'"
>
{{ $t('statusINACTIVE') }}
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
<template
v-if="
listCustomer &&
statsCustomerType.PERS + statsCustomerType.CORP > 0 &&
selectorLabel === 'EMPLOYER'
"
>
<div <div
v-if="listCustomer.length === 0" class="row surface-3 bordered-b justify-between full-width items-center surface-1"
class="row full-width items-center justify-center" style="z-index: 1"
style="min-height: 250px"
> >
<NoData :not-found="!!inputSearch" /> <div class="row q-py-sm q-px-md justify-between full-width">
</div> <q-input
<div for="input-search"
v-if="listCustomer.length !== 0" outlined
class="row full-width customer-row" dense
:style="`grid-template-columns: repeat(${$q.screen.lt.sm ? '2' : $q.screen.lt.md ? '3' : $q.screen.lt.lg ? '5' : '6'}, 1fr)`" style="width: 250px"
style="min-height: 250px" :label="$t('search')"
> class="q-mr-md"
<UsersDetailCardComponent :bg-color="$q.dark.isActive ? 'dark' : 'white'"
v-for="i in listCustomer" v-model="inputSearch"
:key="i.id" debounce="200"
class="hover-card" >
:color="i.customerType === 'CORP' ? 'purple' : 'green'" <template #prepend>
:metadata="{ <q-icon name="mdi-magnify" />
id: i.id, </template>
disabled: i.status === 'ACTIVE' || i.status === 'CREATED', </q-input>
}"
:badge="i.branch"
:list="{
id: i.id,
imageUrl: i.imageUrl,
type: i.customerType,
name: i.customerName === '' ? '-' : i.customerName,
code: i.code,
status: i.status,
detail: [ <div class="row">
{ <q-select
label: 'customerNameTh', v-model="currentStatus"
value: i.customerName, outlined
}, dense
{ option-value="value"
label: 'customerNameEn', option-label="label"
value: i.customerNameEN, class="q-pr-md"
}, map-options
], emit-value
}" :options="[
@enter-card=" { label: $t('all'), value: 'All' },
() => { { label: $t('statusACTIVE'), value: 'ACTIVE' },
currentCustomerName = i.customerName; { label: $t('statusINACTIVE'), value: 'INACTIVE' },
customerType = i.customerType; ]"
currentCustomerUrlImage = i.imageUrl; ></q-select>
currentCustomerId = i.id;
const { branch, ...payload } = i; <q-select
currentCustomer = payload; id="select-field"
isMainPage = false; for="select-field"
} :options="[
" { label: 'กันว่าง 1', value: 'กันว่าง 1' },
@update-card=" { label: 'กันว่าง 2', value: 'กันว่าง 2' },
() => { ]"
if (!listCustomer) return; :display-value="$t('displayField')"
v-model="fieldSelected"
option-label="label"
option-value="value"
map-options
emit-value
outlined
multiple
dense
/>
customerType = i.customerType; <q-btn-toggle
const { branch, ...payload } = i; id="btn-mode"
currentCustomer = payload; v-model="modeView"
dense
currentCustomerId = i.id; class="no-shadow bordered rounded surface-1 q-ml-md"
:toggle-color="$q.dark.isActive ? 'grey-9' : 'grey-2'"
assignFormData(i.id); size="xs"
:options="[
infoDrawerEdit = true; { value: true, slot: 'folder' },
openDialogInputForm('INFO', i.id); { value: false, slot: 'list' },
} ]"
" >
@view-card=" <template v-slot:folder>
() => { <q-icon
const { branch, ...payload } = i; name="mdi-view-grid-outline"
currentCustomer = payload; size="16px"
class="q-px-sm q-py-xs rounded"
currentCustomerId = i.id; :style="{
customerType = i.customerType; color: $q.dark.isActive
assignFormData(i.id); ? modeView
openDialogInputForm('INFO', i.id); ? '#C9D3DB '
} : '#787B7C'
" : modeView
@delete-card="deleteCustomerById(i.id)" ? '#787B7C'
@toggle-status=" : '#C9D3DB',
() => { }"
toggleStatusCustomer( />
i.id, </template>
i.status === 'ACTIVE' ? true : false, <template v-slot:list>
); <q-icon
} name="mdi-format-list-bulleted"
" class="q-px-sm q-py-xs rounded"
/> size="16px"
</div> :style="{
color: $q.dark.isActive
<div ? modeView === false
v-if="listCustomer.length !== 0" ? '#C9D3DB'
class="row justify-between q-pt-md" : '#787B7C'
> : modeView === false
<div class="col-4"> ? '#787B7C'
<div class="row items-center"> : '#C9D3DB',
<div class="app-text-muted" style="width: 80px"> }"
{{ $t('showing') }} />
</div> </template>
<div> </q-btn-toggle>
<q-btn-dropdown dense unelevated :label="pageSize">
<q-list>
<q-item
v-for="v in [10, 30, 50, 100, 500, 1000]"
:key="v"
clickable
v-close-popup
@click="
async () => {
pageSize = v;
await fetchListCustomer();
}
"
>
<q-item-section>
<q-item-label>{{ v }}</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div> </div>
</div> </div>
</div>
<div class="col-4 row justify-center app-text-muted"> <div class="surface-2 bordered-b q-px-md">
{{ <q-tabs
$t('recordsPage', { dense
resultcurrentPage: listCustomer.length, v-model="currentTab"
total: statsCustomerType.PERS + statsCustomerType.CORP, align="left"
}) class="full-height"
}} active-color="info"
</div> >
<div class="col-4 row justify-end"> <q-tab
<PaginationComponent name="employer"
v-model:current-page="currentPageCustomer" @click="
v-model:max-page="maxPageCustomer"
:fetch-data="
async () => { async () => {
await fetchListCustomer(); selectorLabel = 'EMPLOYER';
currentPageCustomer = 1;
inputSearch = '';
currentStatus = 'All';
flowStore.rotate(); flowStore.rotate();
} }
" "
/> >
</div> <div
class="row"
:class="
currentTab === 'employer' ? 'text-bold' : 'app-text-muted'
"
>
{{ $t('EMPLOYER') }}
</div>
</q-tab>
<q-tab
name="employee"
@click="
async () => {
selectorLabel = 'EMPLOYEE';
currentPageEmployee = 1;
inputSearch = '';
currentStatus = 'All';
flowStore.rotate();
}
"
>
<div
class="row"
:class="
currentTab === 'employee' ? 'text-bold' : 'app-text-muted'
"
>
{{ $t('EMPLOYEE') }}
</div>
</q-tab>
</q-tabs>
</div> </div>
</template> </div>
<template <!-- body -->
v-if=" <q-splitter
listEmployee && statsEmployee > 0 && selectorLabel === 'EMPLOYEE' v-model="splitterModel"
" :limits="[15, 80]"
style="width: 100%"
class="col"
> >
<div <template v-slot:before>
v-if="listEmployee.length === 0" <div class="column q-pa-md q-gutter-y-sm">
class="row full-width items-center justify-center" <div v-if="selectorLabel === 'EMPLOYER'">
style="min-height: 250px" <q-item
> v-for="v in fieldCustomer"
<NoData :not-found="!!inputSearch" /> :key="v"
</div> clickable
dense
class="no-padding flex items-center rounded"
active-class="employer-active"
:active="fieldSelectedCustomer.value === v"
@click="fieldSelectedCustomer = { label: v, value: v }"
>
<span class="q-px-md">
{{ $t(v) }}
</span>
</q-item>
</div>
<div v-else>
<q-item
active
dense
clickable
active-class="employer-active"
class="no-padding flex items-center rounded"
>
<span class="q-px-md">
{{ $t('totalEmployee') }}
</span>
</q-item>
</div>
</div>
</template>
<div v-if="listEmployee.length !== 0"> <template v-slot:after>
<PersonCard <div class="column full-height surface-2">
history <!-- employer -->
:list=" <template
listEmployee.map((v: Employee) => ({ v-if="
disabled: v.status === 'INACTIVE', listCustomer &&
img: v.profileImageUrl, statsCustomerType.PERS + statsCustomerType.CORP > 0 &&
id: v.id, selectorLabel === 'EMPLOYER'
name: "
$i18n.locale === 'en-US' >
? `${v.firstNameEN} ${v.lastNameEN}` <div
: `${v.firstName} ${v.lastName}`, v-if="listCustomer.length === 0"
male: v.gender === 'male', class="row col full-width items-center justify-center"
female: v.gender === 'female', style="min-height: 250px"
badge: v.code, >
detail: [ <NoData :not-found="!!inputSearch" />
{ </div>
label: $t('personnelCardNationality'), <div
value: v.nationality, v-if="listCustomer.length !== 0"
}, class="col q-pa-md scroll"
{ >
label: $t('personnelCardAge'), <div
value: calculateAge(v.dateOfBirth), class="row full-width customer-row"
}, :style="`grid-template-columns: repeat(${$q.screen.lt.sm ? '2' : $q.screen.lt.md ? '3' : $q.screen.lt.lg ? '5' : '6'}, 1fr)`"
], style="min-height: 250px"
})) >
" <UsersDetailCardComponent
@history="openHistory" v-for="i in listCustomer"
@update-card="openDialogInputForm" :key="i.id"
@enter-card="openDialogInputForm" class="hover-card"
@delete-card="onDelete" :color="i.customerType === 'CORP' ? 'purple' : 'green'"
@toggle-status="(id, status) => toggleStatusEmployee(id, status)" :metadata="{
/> id: i.id,
disabled:
i.status === 'ACTIVE' || i.status === 'CREATED',
}"
:badge="i.branch"
:list="{
id: i.id,
imageUrl: i.imageUrl,
type: i.customerType,
name: i.customerName === '' ? '-' : i.customerName,
code: i.code,
status: i.status,
<div class="col-4 row justify-center"> detail: [
<div class="col-4"> {
<div class="row items-center"> label: 'customerNameTh',
<div class="app-text-muted" style="width: 80px"> value: i.customerName,
{{ $t('showing') }} },
</div> {
<div> label: 'customerNameEn',
<q-btn-dropdown dense unelevated :label="pageSize"> value: i.customerNameEN,
<q-list> },
<q-item ],
v-for="v in [10, 30, 50, 100, 500, 1000]" }"
:key="v" @enter-card="
clickable () => {
v-close-popup currentCustomerName = i.customerName;
@click=" customerType = i.customerType;
async () => { currentCustomerUrlImage = i.imageUrl;
pageSize = v; currentCustomerId = i.id;
await fetchListEmployee();
} const { branch, ...payload } = i;
" currentCustomer = payload;
> isMainPage = false;
<q-item-section> }
<q-item-label>{{ v }}</q-item-label> "
</q-item-section> @update-card="
</q-item> () => {
</q-list> if (!listCustomer) return;
</q-btn-dropdown>
customerType = i.customerType;
const { branch, ...payload } = i;
currentCustomer = payload;
currentCustomerId = i.id;
assignFormData(i.id);
infoDrawerEdit = true;
openDialogInputForm('INFO', i.id);
}
"
@view-card="
() => {
const { branch, ...payload } = i;
currentCustomer = payload;
currentCustomerId = i.id;
customerType = i.customerType;
assignFormData(i.id);
openDialogInputForm('INFO', i.id);
}
"
@delete-card="deleteCustomerById(i.id)"
@toggle-status="
() => {
toggleStatusCustomer(
i.id,
i.status === 'ACTIVE' ? true : false,
);
}
"
/>
</div> </div>
</div> </div>
</div>
<div class="col-4 row justify-center app-text-muted"> <div
{{ v-if="listCustomer.length !== 0"
$t('recordsPage', { class="row justify-between items-center q-px-md"
resultcurrentPage: listEmployee.length, >
total: statsEmployee, <div class="row col-4 items-center">
}) <div class="app-text-muted" style="width: 80px">
}} {{ $t('showing') }}
</div> </div>
<div class="col-4 row justify-end"> <div>
<PaginationComponent <q-btn-dropdown
v-model:current-page="currentPageEmployee" dense
v-model:max-page="maxPageEmployee" unelevated
:fetch-data=" class="bordered q-pl-md rounded"
async () => { :label="pageSize"
await fetchListEmployee(); style="background-color: var(--surface-1)"
flowStore.rotate(); >
} <q-list>
<q-item
v-for="v in [10, 30, 50, 100, 500, 1000]"
:key="v"
clickable
v-close-popup
@click="
async () => {
pageSize = v;
await fetchListCustomer();
}
"
>
<q-item-section>
<q-item-label>{{ v }}</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div>
<div class="col-4 flex justify-center app-text-muted">
{{
$t('recordsPage', {
resultcurrentPage: listCustomer.length,
total: statsCustomerType.PERS + statsCustomerType.CORP,
})
}}
</div>
<div class="col-4 flex justify-end">
<PaginationComponent
v-model:current-page="currentPageCustomer"
v-model:max-page="maxPageCustomer"
:fetch-data="
async () => {
await fetchListCustomer();
flowStore.rotate();
}
"
/>
</div>
</div>
</template>
<!-- employee -->
<template
v-if="
listEmployee &&
statsEmployee > 0 &&
selectorLabel === 'EMPLOYEE'
"
>
<div
v-if="listEmployee.length === 0"
class="row full-width items-center justify-center col"
style="min-height: 250px"
>
<NoData :not-found="!!inputSearch" />
</div>
<div
class="col column justify-between q-px-md q-pt-md scroll"
v-if="listEmployee.length !== 0"
>
<PersonCard
history
:list="
listEmployee.map((v: Employee) => ({
disabled: v.status === 'INACTIVE',
img: v.profileImageUrl,
id: v.id,
name:
$i18n.locale === 'en-US'
? `${v.firstNameEN} ${v.lastNameEN}`
: `${v.firstName} ${v.lastName}`,
male: v.gender === 'male',
female: v.gender === 'female',
badge: v.code,
detail: [
{
label: $t('personnelCardNationality'),
value: v.nationality,
},
{
label: $t('personnelCardAge'),
value: calculateAge(v.dateOfBirth),
},
],
}))
"
@history="openHistory"
@update-card="openDialogInputForm"
@enter-card="openDialogInputForm"
@delete-card="onDelete"
@toggle-status="
(id, status) => toggleStatusEmployee(id, status)
"
/>
</div>
<div
v-if="listEmployee.length !== 0"
class="row justify-between items-center q-px-md"
>
<div class="row col-4 items-center">
<div class="app-text-muted" style="width: 80px">
{{ $t('showing') }}
</div>
<div>
<q-btn-dropdown
dense
unelevated
:label="pageSize"
class="bordered q-pl-md"
style="background-color: var(--surface-1)"
>
<q-list>
<q-item
v-for="v in [10, 30, 50, 100, 500, 1000]"
:key="v"
clickable
v-close-popup
@click="
async () => {
pageSize = v;
await fetchListEmployee();
}
"
>
<q-item-section>
<q-item-label>{{ v }}</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div>
<div class="col-4 flex justify-center app-text-muted">
{{
$t('recordsPage', {
resultcurrentPage: listEmployee.length,
total: statsEmployee,
})
}}
</div>
<div class="col-4 flex justify-end">
<PaginationComponent
v-model:current-page="currentPageEmployee"
v-model:max-page="maxPageEmployee"
:fetch-data="
async () => {
await fetchListEmployee();
flowStore.rotate();
}
"
/>
</div>
</div>
</template>
<template
v-if="
(statsCustomerType.CORP + statsCustomerType.PERS === 0 &&
selectorLabel === 'EMPLOYER') ||
(statsEmployee === 0 && selectorLabel === 'EMPLOYEE')
"
>
<TooltipComponent
class="self-end"
:title="
selectorLabel === 'EMPLOYER'
? 'customerEmployerTooltipTitle'
: 'customerEmployeeTooltipTitle'
" "
:caption="
selectorLabel === 'EMPLOYER'
? 'customerEmployerTooltipCaption'
: 'customerEmployeeTooltipCaption'
"
imgSrc="personnel-table-"
/> />
</div>
<div
class="row items-center justify-center"
style="flex-grow: 1"
>
<AddButton
:label="
selectorLabel === 'EMPLOYER'
? 'customerEmployerAdd'
: 'customerEmployeeAdd'
"
@trigger="() => openDialogCustomerType()"
/>
</div>
</template>
</div> </div>
</div> </template>
</template> </q-splitter>
<template
v-if="
(statsCustomerType.CORP + statsCustomerType.PERS === 0 &&
selectorLabel === 'EMPLOYER') ||
(statsEmployee === 0 && selectorLabel === 'EMPLOYEE')
"
>
<TooltipComponent
class="self-end"
:title="
selectorLabel === 'EMPLOYER'
? 'customerEmployerTooltipTitle'
: 'customerEmployeeTooltipTitle'
"
:caption="
selectorLabel === 'EMPLOYER'
? 'customerEmployerTooltipCaption'
: 'customerEmployeeTooltipCaption'
"
imgSrc="personnel-table-"
/>
<div class="row items-center justify-center" style="flex-grow: 1">
<AddButton
:label="
selectorLabel === 'EMPLOYER'
? 'customerEmployerAdd'
: 'customerEmployeeAdd'
"
@trigger="() => openDialogCustomerType()"
/>
</div>
</template>
</div> </div>
</div> </div>
<div v-else> <div v-else>
@ -3093,4 +3235,9 @@ watch([inputSearch, currentStatus], async () => {
transform: translateY(-20px); transform: translateY(-20px);
opacity: 0; opacity: 0;
} }
.employer-active {
background-color: hsla(var(--info-bg) / 0.1);
color: hsl(var(--info-bg));
}
</style> </style>