feat: unique id attributes to UI components
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 5s

This commit is contained in:
Aif 2025-11-11 11:01:36 +07:00
parent 637eeab3c2
commit 75d5c7dfe8
13 changed files with 141 additions and 28 deletions

View file

@ -27,11 +27,15 @@ withDefaults(
class="app-text-muted q-pr-sm"
:width="iconSize || '2rem'"
/>
<span class="row col">
<span class="col-12 app-text-muted-2" style="font-size: 10px">
<span :id="`dd-wrapper-${label}`" class="row col">
<span
:id="`dd-label-${label}`"
class="col-12 app-text-muted-2"
style="font-size: 10px"
>
{{ label }}
</span>
<span class="col-12 ellipsis">
<span :id="`dd-value-wrapper-${label}`" class="col-12 ellipsis">
<slot name="value">
<span
:class="{ 'link cursor-pointer': clickable }"
@ -43,7 +47,11 @@ withDefaults(
{{ value }}
<q-tooltip v-if="tooltip" :delay="500">{{ value }}</q-tooltip>
</span>
<span v-else :class="{ 'link cursor-pointer': clickable }">
<span
:id="`dd-value-${label}`"
v-else
:class="{ 'link cursor-pointer': clickable }"
>
<span
v-for="(item, index) in value"
:key="index"

View file

@ -5,6 +5,7 @@ defineEmits<{
(e: 'click', v: MouseEvent): void;
}>();
defineProps<{
id?: string;
icon?: string;
color: string;
iconOnly?: boolean;
@ -18,6 +19,7 @@ defineProps<{
<template>
<button
:id="id"
@click="(e) => $emit('click', e)"
class="main-btn"
:class="{

View file

@ -72,6 +72,7 @@ watch(
</script>
<template>
<q-select
:id="id"
:placeholder="placeholder"
outlined
:clearable

View file

@ -75,7 +75,6 @@ function setDefaultValue() {
</script>
<template>
<SelectInput
for="select-hq-id"
v-model="value"
incremental
:label

View file

@ -234,6 +234,7 @@ watch(
<section class="row q-col-gutter-sm col-12 items-center">
<SelectInput
class="col-md-6 col-12"
id="select-pay-type"
:label="$t('quotation.payType')"
:option="
taskOrder
@ -241,7 +242,6 @@ watch(
: payTypeOption
"
:readonly="readonly || debitNote"
id="pay-type"
:model-value="payType"
@update:model-value="
(v) => {
@ -275,6 +275,7 @@ watch(
</div>
<q-input
v-model="paySplitCount"
id="input-pay-split-count"
:readonly="readonly || payType === 'Split'"
class="col-3"
type="number"
@ -311,6 +312,7 @@ watch(
<q-input
:readonly="readonly"
:label="$t('general.name')"
:id="`input-period-name-${i}`"
v-if="payType === 'SplitCustom'"
v-model="period.name"
class="col q-mx-sm"
@ -320,6 +322,7 @@ watch(
<q-input
:readonly="readonly || payType === 'Split'"
class="col q-mx-sm"
:id="`input-period-amount-${i}`"
:label="$t('quotation.amount')"
:model-value="
amount4Show[i] || commaInput(period.amount.toString())
@ -377,6 +380,7 @@ watch(
<DatePicker
v-if="payType === 'BillFull'"
id="datepicker-bill-date"
:readonly
class="col-12"
:label="$t('quotation.callDueDate')"
@ -484,7 +488,11 @@ watch(
<div class="q-pa-sm row surface-2 items-center text-weight-bold">
{{ $t('quotation.totalPriceBaht') }}
<span class="q-ml-auto" style="color: var(--brand-1)">
<span
class="q-ml-auto"
style="color: var(--brand-1)"
id="value-final-price"
>
{{
payType === 'SplitCustom' && view === View.Invoice
? formatNumberDecimal(Math.max(installmentAmount || 0, 0), 2) || 0

View file

@ -114,6 +114,7 @@ const emit = defineEmits<{
</script>
<template>
<q-table
id="table-task-order"
v-bind="props"
:columns="column"
bordered
@ -133,7 +134,11 @@ const emit = defineEmits<{
:props="props"
>
<q-th v-if="selection !== 'none'">
<q-checkbox v-model="props.selected" size="sm" />
<q-checkbox
id="checkbox-select-all-task"
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) }}
@ -149,13 +154,21 @@ const emit = defineEmits<{
>
<q-tr class="text-center" :class="{ urgent: props.row.urgent }">
<q-td v-if="selection !== 'none'">
<q-checkbox v-model="props.selected" size="sm" />
<q-checkbox
:id="`checkbox-task-${props.row.code}`"
v-model="props.selected"
size="sm"
/>
</q-td>
<q-td v-if="visibleColumns.includes('order')">
{{ props.rowIndex + 1 }}
</q-td>
<q-td v-if="visibleColumns.includes('taskName')" class="text-left">
<div>
<q-td
v-if="visibleColumns.includes('taskName')"
class="text-left"
:id="`task-name-${props.row.code}`"
>
<div :id="`task-name-div-${props.row.code}`">
{{ props.row.taskName || '-' }}
<q-tooltip :delay="300">
{{ props.row.taskName || '-' }}
@ -170,31 +183,51 @@ const emit = defineEmits<{
}}
</div>
</q-td>
<q-td v-if="visibleColumns.includes('issueBranch')">
<q-td
v-if="visibleColumns.includes('issueBranch')"
:id="`task-issue-branch-${props.row.code}`"
>
{{
$i18n.locale === 'eng'
? props.row.registeredBranch.nameEN || '-'
: props.row.registeredBranch.name || '-'
}}
</q-td>
<q-td v-if="visibleColumns.includes('institution')">
<q-td
v-if="visibleColumns.includes('institution')"
:id="`task-institution-${props.row.code}`"
>
{{
$i18n.locale === 'eng'
? props.row.institution.nameEN || '-'
: props.row.institution.name || '-'
}}
</q-td>
<q-td v-if="visibleColumns.includes('createdAt')">
<q-td
v-if="visibleColumns.includes('createdAt')"
:id="`task-created-at-${props.row.code}`"
>
{{ dateFormatJS({ date: props.row.createdAt }) || '-' }}
{{ dateFormatJS({ date: props.row.createdAt, timeOnly: true }) }}
</q-td>
<q-td v-if="visibleColumns.includes('createdBy')" class="text-left">
<q-td
v-if="visibleColumns.includes('createdBy')"
class="text-left"
:id="`task-created-by-${props.row.code}`"
>
{{ getCreatedByName(props.row, $i18n) }}
</q-td>
<q-td v-if="visibleColumns.includes('contactTel')">
<q-td
v-if="visibleColumns.includes('contactTel')"
:id="`task-contact-tel-${props.row.code}`"
>
{{ props.row.contactTel || '-' }}
</q-td>
<q-td v-if="visibleColumns.includes('contactName')" class="text-left">
<q-td
v-if="visibleColumns.includes('contactName')"
class="text-left"
:id="`task-contact-name-${props.row.code}`"
>
{{ props.row.contactName || '-' }}
</q-td>
<q-td v-if="visibleColumns.includes('taskStatus')">
@ -202,11 +235,12 @@ const emit = defineEmits<{
hide-icon
:hsla-color="taskOrderStatus(props.row.taskOrderStatus, 'color')"
:title="$t(taskOrderStatus(props.row.taskOrderStatus, 'status'))"
:id="`badge-task-status-${props.row.code}`"
/>
</q-td>
<q-td v-if="selection === 'none'">
<q-btn
:id="`btn-eye-${props.row.taskName}`"
:id="`btn-view-task-${props.row.code}`"
icon="mdi-eye-outline"
size="sm"
dense
@ -221,7 +255,7 @@ const emit = defineEmits<{
canAccess('taskOrder', 'edit')
"
:hide-delete="!canAccess('taskOrder', 'create')"
:idName="`btn-kebab-${props.row.taskName}`"
:idName="`btn-kebab-${props.row.code}`"
status="'ACTIVE'"
hide-toggle
@view="$emit('view', props.row)"
@ -233,6 +267,7 @@ const emit = defineEmits<{
<q-btn
dense
flat
:id="`btn-sub-row-${props.row.code}`"
class="rounded"
@click.stop="
() => {

View file

@ -25,6 +25,7 @@ const fileData = defineModel<
</script>
<template>
<q-expansion-item
id="expansion-additional-file"
dense
class="overflow-hidden bordered full-width"
switch-toggle-side
@ -41,6 +42,7 @@ const fileData = defineModel<
<main class="q-px-md q-py-sm surface-1">
<UploadFileSection
id="upload-additional-file"
multiple
:layout="$q.screen.gt.sm ? 'column' : 'row'"
:readonly

View file

@ -22,6 +22,7 @@ const contactTel = defineModel<string>('contactTel');
</script>
<template>
<q-expansion-item
id="expansion-document"
default-opened
dense
class="overflow-hidden bordered full-width"
@ -38,6 +39,7 @@ const contactTel = defineModel<string>('contactTel');
<main class="q-px-md q-py-sm surface-1 row q-col-gutter-sm">
<SelectBranch
id="select-issue-branch"
:readonly
class="col-md-4 col-12"
:label="`${$t('taskOrder.issueBranch')}${$i18n.locale === 'tha' ? $t('taskOrder.title') : ''}`"
@ -45,6 +47,7 @@ const contactTel = defineModel<string>('contactTel');
auto-select-on-single
/>
<SelectInstitution
id="select-agencies"
:readonly
required
class="col-md-4 col-12"
@ -55,6 +58,7 @@ const contactTel = defineModel<string>('contactTel');
auto-select-on-single
/>
<DatePicker
id="datepicker-issue-date"
:label="$t('taskOrder.issueDate')"
class="col-md-2 col-6"
:model-value="issueDate || new Date(Date.now())"
@ -62,6 +66,7 @@ const contactTel = defineModel<string>('contactTel');
:disabled="!readonly"
/>
<q-input
id="input-task-code"
:label="$t('taskOrder.code')"
outlined
dense
@ -72,6 +77,7 @@ const contactTel = defineModel<string>('contactTel');
/>
<q-input
id="input-task-name"
:readonly
:label="$t('general.name', { msg: $t('taskOrder.title') })"
outlined
@ -80,6 +86,7 @@ const contactTel = defineModel<string>('contactTel');
v-model="taskName"
/>
<q-input
id="input-contact-name"
:readonly
:label="$t('taskOrder.contactName')"
outlined
@ -88,6 +95,7 @@ const contactTel = defineModel<string>('contactTel');
v-model="contactName"
/>
<q-input
id="input-contact-tel"
:readonly
:label="$t('general.telephone')"
outlined
@ -96,6 +104,7 @@ const contactTel = defineModel<string>('contactTel');
v-model="contactTel"
/>
<q-input
id="input-made-by"
:readonly
:label="$t('taskOrder.madeBy')"
outlined

View file

@ -32,6 +32,7 @@ const summaryPrice = defineModel<{
</script>
<template>
<q-expansion-item
id="expansion-payment"
dense
class="overflow-hidden bordered full-width"
switch-toggle-side
@ -47,6 +48,7 @@ const summaryPrice = defineModel<{
<main class="q-px-md q-py-sm surface-1">
<QuotationFormInfo
id="form-info-payment"
task-order
:task-order-complete="complete"
v-model:pay-type="payType"

View file

@ -111,6 +111,7 @@ function taskOrderStatus(value: TaskStatus) {
</script>
<template>
<q-expansion-item
id="expansion-product-list"
dense
class="overflow-hidden bordered full-width"
switch-toggle-side
@ -127,6 +128,7 @@ function taskOrderStatus(value: TaskStatus) {
>
{{ $t('general.information', { msg: $t('taskOrder.productList') }) }}
<AddButton
id="btn-add-product"
icon-only
@click.stop="$emit('addProduct')"
v-if="!readonly"
@ -215,10 +217,10 @@ function taskOrderStatus(value: TaskStatus) {
/>
</q-td>
<q-td>
<q-td :id="`product-amount-${props.row.product.code}`">
{{ props.row.list.length }}
</q-td>
<q-td class="text-right">
<q-td :id="`product-price-per-unit-${props.row.product.code}`" class="text-right">
{{
formatNumberDecimal(
calcPricePerUnit(props.row.product) +
@ -271,7 +273,11 @@ function taskOrderStatus(value: TaskStatus) {
/>
</q-td>
<!-- before vat -->
<q-td class="text-right" v-if="!creditNote">
<q-td
:id="`product-price-before-vat-${props.row.product.code}`"
class="text-right"
v-if="!creditNote"
>
{{
formatNumberDecimal(
props.row.product.serviceChargeCalcVat
@ -288,7 +294,11 @@ function taskOrderStatus(value: TaskStatus) {
}}
</q-td>
<!-- vat -->
<q-td class="text-right" v-if="!creditNote">
<q-td
:id="`product-vat-${props.row.product.code}`"
class="text-right"
v-if="!creditNote"
>
{{
formatNumberDecimal(
props.row.product.serviceChargeCalcVat
@ -305,7 +315,7 @@ function taskOrderStatus(value: TaskStatus) {
}}
</q-td>
<!-- total -->
<q-td class="text-right">
<q-td :id="`product-total-price-${props.row.product.code}`" class="text-right">
{{
formatNumberDecimal(
calcPrice(props.row.product, props.row.list.length),
@ -339,6 +349,7 @@ function taskOrderStatus(value: TaskStatus) {
<q-tr v-show="currentBtnOpen[props.rowIndex]" :props="props">
<q-td colspan="100%" style="padding: 16px">
<TableEmployee
:id="`table-employee-in-product-${props.row.product.code}`"
:step-on="!creditNote"
:status-on="creditNote"
:rows="props.row.list"

View file

@ -33,6 +33,7 @@ const getToolbarConfig = computed(() => {
</script>
<template>
<q-expansion-item
id="expansion-remark"
dense
class="overflow-hidden bordered full-width"
switch-toggle-side
@ -48,6 +49,7 @@ const getToolbarConfig = computed(() => {
<main class="surface-1 q-pa-md full-width">
<q-editor
id="editor-remark"
dense
:readonly="readonly || !remarkWrite"
:model-value="
@ -90,6 +92,7 @@ const getToolbarConfig = computed(() => {
<template v-if="!readonly" v-slot:toggle>
<div class="text-caption row no-wrap q-px-sm">
<MainButton
id="btn-remark-view"
:solid="!remarkWrite"
icon="mdi-eye-outline"
color="0 0% 40%"
@ -102,6 +105,7 @@ const getToolbarConfig = computed(() => {
{{ $t('general.view', { msg: $t('general.example') }) }}
</MainButton>
<MainButton
id="btn-remark-edit"
:solid="remarkWrite"
icon="mdi-pencil-outline"
color="0 0% 40%"

View file

@ -47,11 +47,17 @@ defineProps<{
<div class="row q-col-gutter-sm q-px-md q-py-sm">
<DataDisplay
class="col-md col-6"
id="dd-recipient"
:label="$t('taskOrder.recipientOrSender')"
>
<template #value>
<q-avatar size="md" class="q-mr-xs">
<q-img class="text-center" :ratio="1" :src="contactUrl">
<q-img
:id="`img-avatar-${contactName}`"
class="text-center"
:ratio="1"
:src="contactUrl"
>
<template #error>
<div
class="no-padding full-width full-height flex items-center justify-center"
@ -59,6 +65,7 @@ defineProps<{
>
<q-img
v-if="gender"
:id="`img-gender-${contactName}`"
:src="
gender === 'male'
? '/no-img-man.png'
@ -67,6 +74,7 @@ defineProps<{
/>
<q-icon
v-else
:id="`icon-avatar-${contactName}`"
size="sm"
name="mdi-account-outline"
style="color: white"
@ -81,18 +89,21 @@ defineProps<{
<DataDisplay
class="col-md col-6"
id="dd-telephone"
:label="$t('general.telephone')"
:value="contactTel || '-'"
/>
<DataDisplay
class="col-md col-6"
id="dd-email"
:label="$t('form.email')"
:value="email || '-'"
/>
<DataDisplay
class="col-md col-6"
id="dd-work-start-date"
:label="$t('taskOrder.workStartDate')"
>
<template #value>
@ -106,6 +117,7 @@ defineProps<{
<DataDisplay
class="col-md col-6"
id="dd-work-submission-date"
:label="$t('taskOrder.workSubmissionDate')"
>
<template #value>
@ -117,9 +129,14 @@ defineProps<{
</template>
</DataDisplay>
<DataDisplay class="col-md col-6" :label="$t('general.status')">
<DataDisplay
id="dd-status"
class="col-md col-6"
:label="$t('general.status')"
>
<template #value>
<BadgeComponent
:id="`badge-status-${contactName}`"
v-if="status"
hide-icon
:title="

View file

@ -98,6 +98,7 @@ function tooltip(id: string) {
<main class="q-py-sm q-px-md scroll">
<q-select
:readonly
id="select-fail-task"
dense
outlined
multiple
@ -131,6 +132,7 @@ function tooltip(id: string) {
>
<template #selected-item="scope">
<q-chip
:id="`chip-fail-task-${taskStatusList[scope.index].code}`"
dense
:removable="!readonly"
@remove="scope.removeAtIndex(scope.index)"
@ -170,6 +172,7 @@ function tooltip(id: string) {
<template #option="scope">
<q-item
:id="`option-fail-task-${scope.opt.request.code}`"
clickable
v-bind="scope.itemProps"
class="row items-start col-12 no-padding"
@ -230,6 +233,7 @@ function tooltip(id: string) {
<SelectInput
:readonly
id="select-fail-type"
:option="[
{
label: $t('taskOrder.documentSubmitFailed'),
@ -260,6 +264,7 @@ function tooltip(id: string) {
<div v-if="failedType === 'other'" class="q-mt-sm rounded">
<q-editor
id="editor-fail-comment"
dense
flat
v-model="failedComment"
@ -279,8 +284,18 @@ function tooltip(id: string) {
</main>
<template #footer v-if="!readonly">
<CancelButton class="q-ml-auto" outlined @click="$emit('close')" />
<SaveButton class="q-ml-sm" solid @click="submit" />
<CancelButton
id="btn-fail-remark-cancel"
class="q-ml-auto"
outlined
@click="$emit('close')"
/>
<SaveButton
id="btn-fail-remark-save"
class="q-ml-sm"
solid
@click="submit"
/>
</template>
</DialogFormContainer>
</template>