refactor: implement request list action dialog and enhance messenger functionality
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 7s

This commit is contained in:
puriphatt 2025-04-10 17:23:19 +07:00
parent a40f9f9775
commit ed5a05709a
5 changed files with 300 additions and 1 deletions

View file

@ -24,6 +24,8 @@ import { RequestData, RequestDataStatus } from 'src/stores/request-list/types';
import { dialogWarningClose } from 'src/stores/utils';
import { CancelButton, SaveButton } from 'src/components/button';
import { getRole } from 'src/services/keycloak';
import FloatingActionButton from 'src/components/FloatingActionButton.vue';
import RequestListAction from './RequestListAction .vue';
const $q = useQuasar();
const navigatorStore = useNavigator();
@ -32,6 +34,7 @@ const requestListStore = useRequestList();
const { t } = useI18n();
const { data, stats, page, pageMax, pageSize } = storeToRefs(requestListStore);
const requestListActionData = ref<RequestData[]>();
const refFilter = ref<InstanceType<typeof QSelect>>();
// NOTE: Variable
@ -45,6 +48,7 @@ const pageState = reactive({
rejectCancelDialog: false,
rejectCancelReason: '',
requestId: '',
requestListActionDialog: false,
});
const fieldSelectedOption = computed(() => {
@ -131,6 +135,33 @@ async function submitRejectCancel() {
}
}
async function openRequestListDialog() {
const ret = await requestListStore.getRequestDataList({
page: 1,
pageSize: 999,
incomplete: true,
});
console.log(ret);
if (ret) {
requestListActionData.value = ret.result;
}
pageState.requestListActionDialog = true;
}
async function submitRequestListAction(data: {
form: { responsibleUserLocal: boolean; responsibleUserId: string };
selected: RequestData[];
}) {
const res = await requestListStore.updateMessenger(
data.selected.map((v) => v.id),
data.form.responsibleUserId,
);
if (res) pageState.requestListActionDialog = false;
}
onMounted(async () => {
pageState.gridView = $q.screen.lt.md ? true : false;
navigatorStore.current.title = 'requestList.title';
@ -147,6 +178,13 @@ watch([() => pageState.inputSearch, () => pageState.statusFilter], () => {
});
</script>
<template>
<FloatingActionButton
hide-icon
style="z-index: 999"
icon="mdi-account-outline"
@click.stop="openRequestListDialog"
></FloatingActionButton>
<div class="column full-height no-wrap">
<!-- SEC: stat -->
<section class="text-body-2 q-mb-xs flex items-center">
@ -485,6 +523,13 @@ watch([() => pageState.inputSearch, () => pageState.statusFilter], () => {
/>
</template>
</DialogFormContainer>
<RequestListAction
v-if="requestListActionData"
v-model="pageState.requestListActionDialog"
:request-list="requestListActionData"
@submit="submitRequestListAction"
/>
</div>
</template>
<style></style>

View file

@ -13,6 +13,7 @@ const props = defineProps<{
readonly?: boolean;
step: Step;
responsibleAreaDistrictId?: string;
defaultMessenger?: string;
}>();
const emit = defineEmits<{
@ -85,7 +86,8 @@ function assignToForm() {
companyDuty: attributesForm.value.companyDuty ?? false,
companyDutyCost: attributesForm.value.companyDutyCost ?? 30,
responsibleUserLocal: attributesForm.value.responsibleUserLocal ?? true,
responsibleUserId: attributesForm.value.responsibleUserId ?? '',
responsibleUserId:
attributesForm.value.responsibleUserId || props.defaultMessenger,
individualDuty: attributesForm.value.individualDuty ?? false,
individualDutyCost: attributesForm.value.individualDutyCost ?? 10,
}),

View file

@ -0,0 +1,226 @@
<script setup lang="ts">
import { reactive, ref } from 'vue';
import { RequestData } from 'src/stores/request-list';
import { DialogContainer, DialogHeader } from 'src/components/dialog';
import {
BackButton,
CancelButton,
MainButton,
SaveButton,
} from 'src/components/button';
import TableProductAndService from 'src/components/shared/table/TableProductAndService.vue';
import { Product } from 'src/stores/product-service/types';
import FormResponsibleUser from './FormResponsibleUser.vue';
import FormGroupHead from './FormGroupHead.vue';
import TableRequestList from './TableRequestList.vue';
import { column } from './constants';
defineProps<{
requestList: RequestData[];
}>();
defineEmits<{
(
e: 'submit',
data: {
form: { responsibleUserLocal: boolean; responsibleUserId: string };
selected: RequestData[];
},
): void;
}>();
enum Step {
RequestList = 1,
Configure = 2,
}
const open = defineModel<boolean>({ default: false });
const step = ref<Step>(Step.RequestList);
const selected = ref<RequestData[]>([]);
const form = reactive({
responsibleUserLocal: false,
responsibleUserId: '',
});
function reset() {
step.value = Step.RequestList;
selected.value = [];
form.responsibleUserLocal = false;
form.responsibleUserId = '';
}
function prev() {
step.value = Step.RequestList;
console.log(selected.value[0]);
}
</script>
<template>
<DialogContainer v-model="open" :onOpen="reset">
<template #header>
<DialogHeader :title="$t('requestList.action.title')" />
</template>
<div class="surface-0 q-pa-md">
<div class="stepper-wrapper">
<div class="stepper">
<template
v-for="(label, i) in [
$t('menu.product'),
$t('requestList.action.configure'),
]"
:key="i"
>
<span class="step" :class="{ ['step__active']: step > i }">
<span class="step-outer"><span class="step-inner" /></span>
<span class="step-label">{{ label }}</span>
</span>
<span
class="step-connector"
:class="{ ['step-connector__active']: step > i + 1 }"
/>
</template>
</div>
</div>
</div>
<div class="surface-1 q-pa-md col full-width scroll">
<TableRequestList
v-if="step === Step.RequestList"
v-model:selected="selected"
hide-action
hide-view
checkable
:columns="column"
:rows="requestList"
:visible-columns="[...column.map((col) => col.name)]"
/>
<template v-if="step === Step.Configure">
<q-expansion-item
dense
class="overflow-hidden bordered full-width"
switch-toggle-side
style="border-radius: var(--radius-2)"
expand-icon="mdi-chevron-down-circle"
header-class="surface-1 q-py-sm text-medium text-body1"
default-opened
>
<template #header>
<span>
{{ $t('requestList.employeeMessenger') }}
</span>
</template>
<FormGroupHead>
{{
$t('general.select', { msg: $t('requestList.employeeMessenger') })
}}
</FormGroupHead>
<FormResponsibleUser
v-model:responsible-user-id="form.responsibleUserId"
v-model:responsible-user-local="form.responsibleUserLocal"
/>
</q-expansion-item>
</template>
</div>
<template #footer>
<div class="q-gutter-x-xs q-ml-auto">
<CancelButton
v-if="step === Step.RequestList"
id="btn-cancel"
outlined
@click="
reset();
open = false;
"
/>
<BackButton
v-if="step === Step.Configure"
id="btn-back"
outlined
@click="prev"
/>
<MainButton
icon="mdi-check"
color="207 96% 32%"
solid
id="btn-next"
v-if="step === Step.RequestList"
@click="step = Step.Configure"
>
{{ $t('general.next') }}
</MainButton>
<SaveButton
v-if="step === Step.Configure"
id="btn-save"
solid
@click="$emit('submit', { form, selected })"
/>
</div>
</template>
</DialogContainer>
</template>
<style lang="scss" scoped>
.stepper {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1.5rem;
margin-inline: 25%;
& > .step {
--__color: var(--gray-5);
display: flex;
flex-direction: column;
align-items: center;
position: relative;
gap: 0.25rem;
&.step__active {
--__color: var(--brand-1);
}
& > .step-label {
position: absolute;
font-weight: 600;
color: var(--__color);
white-space: nowrap;
top: 2rem;
}
& > .step-outer {
display: inline-flex;
border: 2px solid var(--__color);
border-radius: 50%;
width: 1.5rem;
height: 1.5rem;
align-items: center;
justify-content: center;
& > .step-inner {
display: inline-block;
border-radius: 50%;
background-color: var(--__color);
width: 0.7rem;
height: 0.7rem;
}
}
}
& > .step-connector {
display: block;
border-bottom: 2px solid var(--gray-5);
flex-grow: 1;
&.step-connector__active {
border-color: var(--brand-1);
}
&:last-child {
display: none;
}
}
}
</style>

View file

@ -873,6 +873,11 @@ async function submitRejectCancel() {
:readonly="
data.requestDataStatus === RequestDataStatus.Canceled
"
:default-messenger="
value.stepStatus[pageState.currentStep - 1]
? undefined
: data.defaultMessengerId
"
:step="{
step: pageState.currentStep,
requestWorkId: value.id || '',

View file

@ -21,6 +21,8 @@ const props = withDefaults(
grid?: boolean;
visibleColumns?: string[];
hideAction?: boolean;
hideView?: boolean;
checkable?: boolean;
}>(),
{
row: () => [],
@ -36,6 +38,8 @@ defineEmits<{
(e: 'rejectCancel', data: RequestData): void;
}>();
const selected = defineModel<unknown[]>('selected');
function responsiblePerson(quotation: QuotationFull): CreatedBy[] | undefined {
const productServiceList = quotation.productServiceList;
const tempPerson: CreatedBy[] = [];
@ -106,12 +110,25 @@ function getEmployeeName(
card-container-class="q-col-gutter-sm"
:rows-per-page-options="[0]"
class="full-width"
selection="multiple"
v-model:selected="selected"
:selected-rows-label="
(n) =>
$t('general.selected', {
number: n,
msg: $t('general.list'),
})
"
:no-data-label="$t('general.noDataTable')"
>
<template v-slot:header="props">
<q-tr
style="background-color: hsla(var(--info-bg) / 0.07)"
:props="props"
>
<q-th v-if="checkable">
<q-checkbox v-model="props.selected" size="sm" />
</q-th>
<q-th v-for="col in props.cols" :key="col.name" :props="props">
{{ col.label && $t(col.label) }}
</q-th>
@ -128,6 +145,9 @@ function getEmployeeName(
:class="{ urgent: props.row.quotation.urgent, dark: $q.dark.isActive }"
class="text-center"
>
<q-td v-if="checkable">
<q-checkbox v-model="props.selected" size="sm" />
</q-td>
<q-td v-if="visibleColumns.includes('order')">
{{ props.rowIndex + 1 }}
</q-td>
@ -215,6 +235,7 @@ function getEmployeeName(
</q-td>
<q-td class="text-right">
<q-btn
v-if="!hideView"
:id="`btn-eye-${props.row.code}`"
icon="mdi-eye-outline"
size="sm"