jws-frontend/src/components/shared/table/TableWorker.vue
Aif a1ed625d32
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 5s
feat: for
2025-10-20 11:24:28 +07:00

229 lines
5.7 KiB
Vue

<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import { Lang } from 'src/utils/ui';
import { QTableSlots, QTableColumn } from 'quasar';
import { Employee } from 'src/stores/employee/types';
import { calculateAge, dateFormatJS } from 'src/utils/datetime';
import useOptionStore from 'stores/options';
const selected = defineModel<Employee[]>('selected');
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
const { locale } = useI18n();
const optionStore = useOptionStore();
defineEmits<{
(e: 'create'): void;
}>();
type ExclusiveProps = {
grid?: boolean;
disabledWorkerId?: string[];
rows: Employee[];
};
const columns = [
{
name: '#check',
align: 'left',
label: '',
field: (_: any) => '#check',
},
{
name: 'order',
align: 'center',
label: 'general.order',
field: (e: Employee & { _index: number }) => e._index + 1,
},
{
name: 'foreignRefNo',
align: 'center',
label: 'quotation.foreignRefNo',
field: (v: Employee) =>
v.employeePassport !== undefined
? v.employeePassport[0]?.number || '-'
: '-',
},
{
name: 'employeeName',
align: 'left',
label: 'quotation.employeeName',
field: (v: Employee) =>
locale.value === Lang.English
? `${v.firstNameEN} ${v.lastNameEN}`
: v.firstName
? `${v.firstName} ${v.lastName}`
: `${v.firstNameEN} ${v.lastNameEN}`,
},
{
name: 'birthDate',
align: 'left',
label: 'general.birthDate',
field: (v: Employee) => dateFormatJS({ date: v.dateOfBirth }),
},
{
name: 'age',
align: 'left',
label: 'general.age',
field: (v: Employee) => calculateAge(v.dateOfBirth),
},
{
name: 'nationality',
align: 'left',
label: 'general.nationality',
field: (v: Employee) => optionStore.mapOption(v.nationality),
},
{
name: 'documentExpireDate',
align: 'left',
label: 'quotation.documentExpireDate',
field: (v: Employee) =>
v.employeePassport !== undefined &&
v.employeePassport[0]?.expireDate !== undefined
? dateFormatJS({ date: v.employeePassport[0]?.expireDate })
: '-',
},
] satisfies QTableColumn[];
const props = defineProps<ExclusiveProps>();
function getEmployeeImageUrl(item: Employee) {
if (item.selectedImage) {
return `${API_BASE_URL}/employee/${item.id}/image/${item.selectedImage}`;
}
// NOTE: static image
return `/images/employee-avatar-${item.gender}.png`;
}
function handleUpdate() {
if (selected.value?.length === 0) {
selected.value = props.rows?.filter(
(v) => !props.disabledWorkerId?.includes(v.id),
);
} else {
selected.value = [];
}
}
function selectedIndex(item: Employee) {
return selected.value?.findIndex((v) => v.id === item.id);
}
</script>
<template>
<q-table
v-model:selected="selected"
:rows-per-page-options="[0]"
:rows="
rows.map((data, i) => ({
...data,
_index: i,
}))
"
:grid
:columns
hide-bottom
bordered
flat
hide-pagination
selection="multiple"
card-container-class="q-col-gutter-sm"
class="full-width"
row-key="id"
>
<template v-slot:header="props">
<q-tr
style="background-color: hsla(var(--info-bg) / 0.07)"
:props="props"
>
<q-th v-for="col in columns" :key="col.name" :props="props">
<template v-if="!col.name.startsWith('#')">
{{ $t(col.label) }}
</template>
<template v-if="col.name === '#check'">
<q-checkbox
id="select-worker-all"
for="select-worker-all"
v-model="props.selected"
@update:model-value="(v) => handleUpdate()"
size="sm"
/>
</template>
</q-th>
</q-tr>
</template>
<template
v-slot:body="props: {
row: Employee & { _index: number };
} & Omit<Parameters<QTableSlots['body']>[0], 'row'>"
>
<q-tr
:class="{
dark: $q.dark.isActive,
'selectable-item__disabled': disabledWorkerId?.some(
(id) => id === props.row.id,
),
}"
class="text-center"
>
<q-td v-for="col in columns" :align="col.align" :key="col.name">
<!-- NOTE: custom column will starts with # -->
<template v-if="!col.name.startsWith('#')">
<q-avatar
v-if="col.name === 'employeeName'"
class="q-mr-sm"
size="md"
>
<q-img
:src="getEmployeeImageUrl(props.row)"
:ratio="1"
class="text-center"
/>
</q-avatar>
<span>
{{
typeof col.field === 'string'
? props.row[col.field as keyof Employee]
: col.field(props.row)
}}
</span>
</template>
<template
v-if="
col.name === '#check' &&
!disabledWorkerId?.some((id) => id === props.row.id)
"
>
<q-checkbox
v-model="props.selected"
size="sm"
:id="`select-worker-${props.row.firstName}`"
:for="`select-worker-${props.row.firstName}`"
/>
</template>
</q-td>
</q-tr>
</template>
<template v-slot:item="props: { row: Employee; rowIndex: number }">
<slot
name="grid"
:index="props.rowIndex"
:item="{ ...props.row, _selectedIndex: selectedIndex(props.row) }"
/>
</template>
</q-table>
</template>
<style scoped>
.selectable-item__disabled {
filter: grayscale(1);
opacity: 0.5;
& :deep(*) {
cursor: not-allowed;
}
}
</style>