Compare commits

...

15 commits

Author SHA1 Message Date
Thanaphon Frappet
543c28e162 Merge branch 'develop'
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 12s
2025-05-02 10:25:03 +07:00
Methapon2001
bcd54813d1 fix: select multiple when remove
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 9s
2025-04-30 17:03:07 +07:00
puriphatt
2b78abcd3b fix: adjust layout classes for responsive design on ViewPage
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 7s
2025-04-30 16:58:40 +07:00
puriphatt
4d023a7c7c feat: add smooth scrolling on validation error for forms
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 8s
2025-04-30 16:15:08 +07:00
puriphatt
70e9755952 fix: ensure contactName and contactTel default to empty string if undefined
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 7s
2025-04-30 15:34:30 +07:00
puriphatt
9a57056ffa refactor: remove unused workerList and reset selectedWorker on branch change
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 7s
2025-04-30 15:11:12 +07:00
Methapon2001
8354b6b40a Merge branch 'develop' 2025-04-30 12:42:24 +07:00
puriphatt
4d8eebdd04 feat: enable responsible user filtering and enhance button states for improved UX
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 10s
2025-04-30 12:04:18 +07:00
Methapon2001
5ec924fe14 Merge branch 'develop'
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 8s
2025-04-30 12:02:12 +07:00
Methapon2001
0871a8b899 feat: only allow current responsible user and group 2025-04-30 12:02:05 +07:00
puriphatt
7a2be56ef4 refactor: improve checkbox and badge behavior based on notification state
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 8s
2025-04-30 11:13:45 +07:00
puriphatt
c4c4b76973 refactor: adjust button sizes and padding for improved layout consistency
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 7s
2025-04-30 10:23:47 +07:00
Methapon2001
2192041e35 Merge branch 'develop'
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 7s
2025-04-29 13:08:13 +07:00
Methapon2001
c1ffbef565 feat: auto go into first entry if only one
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 9s
2025-04-29 13:08:02 +07:00
Thanaphon Frappet
46de2412df refactor: handle role
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 8s
2025-04-29 11:22:10 +07:00
13 changed files with 95 additions and 31 deletions

View file

@ -42,6 +42,15 @@ defineProps<{
const modal = defineModel('modal', { default: false }); const modal = defineModel('modal', { default: false });
const currentTab = defineModel<string>('currentTab'); const currentTab = defineModel<string>('currentTab');
async function onValidationError(ref: any) {
const el = ref.$el as Element;
el.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'nearest',
});
}
</script> </script>
<template> <template>
<q-dialog <q-dialog
@ -60,6 +69,7 @@ const currentTab = defineModel<string>('currentTab');
@submit.prevent @submit.prevent
@validation-success="submit" @validation-success="submit"
class="column full-height" class="column full-height"
@validation-error="onValidationError"
> >
<!-- header --> <!-- header -->
<div <div

View file

@ -46,6 +46,15 @@ function reset() {
myForm.value.resetValidation(); myForm.value.resetValidation();
} }
} }
async function onValidationError(ref: any) {
const el = ref.$el as Element;
el.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'nearest',
});
}
</script> </script>
<template> <template>
<q-drawer <q-drawer
@ -66,6 +75,7 @@ function reset() {
greedy greedy
@submit.prevent @submit.prevent
@validation-success="submit" @validation-success="submit"
@validation-error="onValidationError"
> >
<div <div
class="column justify-between full-height" class="column justify-between full-height"

View file

@ -23,6 +23,15 @@ function update(value: boolean) {
} }
} }
async function onValidationError(ref: any) {
const el = ref.$el as Element;
el.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'nearest',
});
}
const state = defineModel({ default: false }); const state = defineModel({ default: false });
</script> </script>
@ -41,6 +50,7 @@ const state = defineModel({ default: false });
}" }"
> >
<q-form <q-form
@validation-error="onValidationError"
@submit.prevent="(e) => $emit('submit', e)" @submit.prevent="(e) => $emit('submit', e)"
@reset="$emit('reset')" @reset="$emit('reset')"
greedy greedy

View file

@ -72,7 +72,11 @@ onMounted(async () => {
:option="selectOptions" :option="selectOptions"
:hide-selected="false" :hide-selected="false"
:fill-input="false" :fill-input="false"
:rules="[(v: string) => !!v || $t('form.error.required')]" :rules="[
(v: string) => {
return !!v?.length || $t('form.error.required');
},
]"
@filter="filter" @filter="filter"
> >
<template #before-options v-if="creatable"> <template #before-options v-if="creatable">

View file

@ -10,10 +10,11 @@ import { onMounted, watch } from 'vue';
import { useManualStore } from 'src/stores/manual'; import { useManualStore } from 'src/stores/manual';
import { useNavigator } from 'src/stores/navigator'; import { useNavigator } from 'src/stores/navigator';
import { Icon } from '@iconify/vue/dist/iconify.js'; import { Icon } from '@iconify/vue/dist/iconify.js';
import { useRoute } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
// NOTE: Variable // NOTE: Variable
const route = useRoute(); const route = useRoute();
const router = useRouter();
const manualStore = useManualStore(); const manualStore = useManualStore();
const navigatorStore = useNavigator(); const navigatorStore = useNavigator();
const { dataManual, dataTroubleshooting } = storeToRefs(manualStore); const { dataManual, dataTroubleshooting } = storeToRefs(manualStore);
@ -33,6 +34,16 @@ watch(
if (route.name === 'Troubleshooting') { if (route.name === 'Troubleshooting') {
const res = await manualStore.getTroubleshooting(); const res = await manualStore.getTroubleshooting();
dataTroubleshooting.value = res ? res : []; dataTroubleshooting.value = res ? res : [];
if (
res.length &&
res.length === 1 &&
res[0].page &&
res[0].page.length === 1
) {
router.replace(
`/troubleshooting/${res[0].category}/${res[0].page[0].name}`,
);
}
} }
}, },
{ immediate: true }, { immediate: true },

View file

@ -116,8 +116,8 @@ async function scrollTo(id: string) {
<template> <template>
<main <main
class="full-height q-gutter-sm" class="full-height q-gutter-sm no-wrap"
:class="{ 'row reverse': $q.screen.gt.xs, column: $q.screen.xs }" :class="{ column: !toc && $q.screen.lt.md, 'row reverse': $q.screen.gt.sm }"
> >
<section <section
v-if="toc" v-if="toc"
@ -168,7 +168,7 @@ async function scrollTo(id: string) {
</q-list> </q-list>
</section> </section>
<section v-if="!toc && $q.screen.xs"> <section v-if="!toc && $q.screen.lt.md">
<q-btn <q-btn
dense dense
class="full-width text-capitalize" class="full-width text-capitalize"
@ -181,7 +181,7 @@ async function scrollTo(id: string) {
</section> </section>
<section <section
v-if="content || (!toc && $q.screen.xs)" v-if="$q.screen.gt.xs || (!toc && $q.screen.xs)"
ref="wrapper" ref="wrapper"
class="markdown col scroll full-height rounded" class="markdown col scroll full-height rounded"
> >

View file

@ -94,21 +94,22 @@ onMounted(async () => {
<template> <template>
<main class="column full-height no-wrap"> <main class="column full-height no-wrap">
<div class="surface-1 col bordered rounded column"> <div class="surface-1 col bordered rounded column">
<div class="q-px-lg q-py-xs row items-center"> <div class="q-py-xs row items-center" style="padding-inline: 38px">
<q-checkbox <q-checkbox
size="xs" size="xs"
:model-value="selectedNoti.length === noti.length" :model-value="noti.length > 0 && selectedNoti.length === noti.length"
class="q-px-sm" :disable="noti.length === 0"
@click="toggleSelection('', true)" @click="toggleSelection('', true)"
/> />
<q-separator vertical inset spaced="md" />
<q-btn <q-btn
v-if="selectedNoti.length === 0" v-if="selectedNoti.length === 0"
icon="mdi-refresh" icon="mdi-refresh"
rounded rounded
flat flat
dense dense
size="xs" size="sm"
class="app-text-muted-2 q-ml-sm q-mt-xs" class="app-text-muted-2 q-mt-xs"
@click="async () => await fetchNoti()" @click="async () => await fetchNoti()"
> >
<q-tooltip>Refresh</q-tooltip> <q-tooltip>Refresh</q-tooltip>
@ -119,8 +120,8 @@ onMounted(async () => {
rounded rounded
flat flat
dense dense
size="xs" size="sm"
class="app-text-muted-2 q-ml-sm" class="app-text-muted-2"
@click="async () => await deleteNoti()" @click="async () => await deleteNoti()"
> >
<q-tooltip>{{ $t('general.delete') }}</q-tooltip> <q-tooltip>{{ $t('general.delete') }}</q-tooltip>
@ -131,7 +132,7 @@ onMounted(async () => {
rounded rounded
flat flat
dense dense
size="xs" size="sm"
class="app-text-muted-2 q-mx-sm" class="app-text-muted-2 q-mx-sm"
@click="async () => await markAsRead()" @click="async () => await markAsRead()"
> >
@ -177,7 +178,13 @@ onMounted(async () => {
<q-badge <q-badge
rounded rounded
class="q-ml-md" class="q-ml-md"
style="background: hsl(var(--info-bg))" :color="
(tab.value === 'all'
? noti.length
: noti.filter((v) => !v.read).length) > 0
? 'info'
: 'grey'
"
> >
{{ {{
tab.value === 'all' tab.value === 'all'

View file

@ -555,7 +555,6 @@ async function triggerChangeStatus(id: string, status: string) {
async function assignFormData(idEdit: string) { async function assignFormData(idEdit: string) {
if (!userData.value) return; if (!userData.value) return;
const foundUser = userData.value.result.find((user) => user.id === idEdit); const foundUser = userData.value.result.find((user) => user.id === idEdit);
console.log(foundUser);
if (foundUser) { if (foundUser) {
currentUser.value = foundUser; currentUser.value = foundUser;
@ -600,8 +599,8 @@ async function assignFormData(idEdit: string) {
responsibleArea: foundUser.responsibleArea, responsibleArea: foundUser.responsibleArea,
status: foundUser.status, status: foundUser.status,
selectedImage: foundUser.selectedImage, selectedImage: foundUser.selectedImage,
contactName: foundUser.contactName, contactName: foundUser.contactName || '',
contactTel: foundUser.contactTel, contactTel: foundUser.contactTel || '',
licenseExpireDate: licenseExpireDate:
(foundUser.licenseExpireDate && (foundUser.licenseExpireDate &&
new Date(foundUser.licenseExpireDate)) || new Date(foundUser.licenseExpireDate)) ||

View file

@ -2506,6 +2506,7 @@ watch(
/> />
<KebabAction <KebabAction
v-if="actionDisplay"
:disable-delete="props.row.status !== 'CREATED'" :disable-delete="props.row.status !== 'CREATED'"
:status="props.row.status" :status="props.row.status"
:id-name="props.row.name" :id-name="props.row.name"
@ -3252,6 +3253,7 @@ watch(
" "
/> />
<KebabAction <KebabAction
v-if="actionDisplay"
:use-copy="productAndServiceTab === 'service'" :use-copy="productAndServiceTab === 'service'"
:status="props.row.status" :status="props.row.status"
:id-name="props.row.name" :id-name="props.row.name"

View file

@ -198,7 +198,6 @@ const selectedWorkerItem = computed(() => {
})), })),
]; ];
}); });
const workerList = ref<Employee[]>([]);
const firstCodePayment = ref(''); const firstCodePayment = ref('');
const selectedProductGroup = ref(''); const selectedProductGroup = ref('');
const selectedInstallmentNo = ref<number[]>([]); const selectedInstallmentNo = ref<number[]>([]);
@ -1059,12 +1058,7 @@ watch(
() => quotationFormData.value.customerBranchId, () => quotationFormData.value.customerBranchId,
async (v) => { async (v) => {
if (!v) return; if (!v) return;
selectedWorker.value = [];
const retEmp = await customerStore.fetchBranchEmployee(v, {
passport: true,
});
if (retEmp) workerList.value = retEmp.data.result;
}, },
); );

View file

@ -143,6 +143,7 @@ async function openRequestListDialog() {
page: 1, page: 1,
pageSize: 999, pageSize: 999,
incomplete: true, incomplete: true,
responsibleOnly: true,
}); });
if (ret) { if (ret) {

View file

@ -153,6 +153,7 @@ watch(
@click="prev" @click="prev"
/> />
<MainButton <MainButton
:disabled="selected.length === 0"
icon="mdi-check" icon="mdi-check"
color="207 96% 32%" color="207 96% 32%"
solid solid
@ -164,6 +165,7 @@ watch(
</MainButton> </MainButton>
<SaveButton <SaveButton
v-if="step === Step.Configure" v-if="step === Step.Configure"
:disabled="form.responsibleUserId === ''"
id="btn-save" id="btn-save"
solid solid
@click="$emit('submit', { form, selected })" @click="$emit('submit', { form, selected })"

View file

@ -1005,7 +1005,21 @@ function toEmployee(employee: RequestData['employee']) {
:label="$t('dialog.action.close')" :label="$t('dialog.action.close')"
/> />
<MainButton <MainButton
v-if="productsList.some((v) => v._messengerExpansion)" v-if="
productsList.some((v) => v._messengerExpansion) &&
!(
data.requestDataStatus === RequestDataStatus.Canceled ||
(responsibleList &&
!responsibleList[pageState.currentStep]?.user.find(
(v) => v.id === getUserId(),
) &&
!responsibleList[pageState.currentStep]?.group.some((v) =>
currentUserGroup.includes(v),
)) ||
(!!responsibleList[pageState.currentStep]?.user?.length &&
!!responsibleList[pageState.currentStep]?.user?.length)
)
"
solid solid
icon="mdi-account-outline" icon="mdi-account-outline"
color="207 96% 32%" color="207 96% 32%"