feat(employee): add partial filter functionality to employee list, needs more work.

This commit is contained in:
Nicolas Drolet 2025-12-04 17:18:22 -05:00
parent fa62fb5ba1
commit 5bdf1e5eaa
7 changed files with 155 additions and 60 deletions

View File

@ -9,6 +9,7 @@ export default {
role: "Role",
supervisor: "Supervisor",
company: "Company",
is_supervisor: "is a supervisor",
},
},
@ -36,7 +37,7 @@ export default {
usage_description: "You can use roles to enable preset modules, add or remove modules individually, or both",
},
filter: {
show_terminated: "Show inactive employees",
hide_terminated: "Hide inactive employees",
sort_by_tags: "sort by tags",
},
},

View File

@ -9,6 +9,7 @@ export default {
role: "rôle",
supervisor: "superviseur",
company: "Compagnie",
is_supervisor: "est un superviseur",
},
},
@ -36,7 +37,7 @@ export default {
usage_description: "Vous pouvez utiliser les rôles pour sélectionner des modules prédéfinis, enlever ou ajouter des modules individuellement, ou les deux",
},
filter: {
show_terminated: "Afficher les employés inactifs",
hide_terminated: "Cacher les employés inactifs",
sort_by_tags: "filtrer par identifiants",
},
},

View File

@ -5,11 +5,12 @@
import EmployeeListTableItem from 'src/modules/employee-list/components/employee-list-table-item.vue';
import { onMounted, ref } from 'vue';
import { date, type QTableColumn } from 'quasar';
import { useUiStore } from 'src/stores/ui-store';
import { useEmployeeStore } from 'src/stores/employee-store';
import { useTimesheetStore } from 'src/stores/timesheet-store';
import { useEmployeeListApi } from 'src/modules/employee-list/composables/use-employee-api';
import { employee_list_columns } from 'src/modules/employee-list/models/employee-profile.models';
import { employee_list_columns, type EmployeeProfile, type EmployeeListFilters } from 'src/modules/employee-list/models/employee-profile.models';
const employee_list_api = useEmployeeListApi();
const employee_store = useEmployeeStore();
@ -17,7 +18,46 @@
const ui_store = useUiStore();
const is_loading_list = ref<boolean>(true);
const filter = ref("");
const filters = ref<EmployeeListFilters>({
search_bar_string: '',
hide_inactive_users: true,
});
const filterEmployeeRows = (
rows: readonly EmployeeProfile[],
terms: EmployeeListFilters,
_cols: readonly QTableColumn<EmployeeProfile>[],
_getCellValue: (col: QTableColumn<EmployeeProfile>, row: EmployeeProfile) => unknown
): EmployeeProfile[] => {
let active_rows: EmployeeProfile[] = Array.from(rows);
console.log('active rows at start: ', active_rows);
if (terms.hide_inactive_users === true) {
active_rows = active_rows.filter(row =>
row.last_work_day === null
// {
// if (row.last_work_day === null) return true;
// const inactive_date = date.extractDate(row.last_work_day!, 'YYYY-MM-DD');
// const inactive_date_limit = new Date();
// inactive_date_limit.setDate(inactive_date.getDate() + 14)
// return inactive_date_limit > inactive_date
// }
);
}
let filtered_rows: EmployeeProfile[] = Array.from(active_rows);
if (terms.search_bar_string.length > 0) {
console.log('more filtering!!')
const search_terms = terms.search_bar_string.split(',');
filtered_rows = active_rows.filter(row => Object.values(row).some(value => search_terms.includes(value)));
};
return filtered_rows;
}
onMounted(async () => {
is_loading_list.value = true;
@ -38,7 +78,8 @@
:columns="employee_list_columns"
row-key="name"
:rows-per-page-options="[0]"
:filter="filter"
:filter="filters"
:filter-method="filterEmployeeRows"
class="bg-transparent no-shadow sticky-header-table"
:style="$q.screen.lt.md ? '' : 'width: 80vw;'"
:table-class="$q.dark.isActive ? 'q-py-none q-mx-md rounded-10 bg-dark shadow-10' : 'q-py-none q-mx-md rounded-10 bg-white shadow-10'"
@ -51,7 +92,6 @@
:no-results-label="$t('shared.error.no_search_results')"
:loading-label="$t('shared.label.loading')"
:visible-columns="['first_name', 'email', 'company', 'supervisor_full_name', 'company_name', 'job_title']"
@row-click="() => console.log('click!')"
>
<template #top>
<div class="row full-width q-mb-sm">
@ -80,7 +120,7 @@
]"
/>
<q-input
v-model="filter"
v-model="filters.search_bar_string"
outlined
dense
rounded
@ -97,6 +137,14 @@
</template>
</q-input>
</div>
<div class="row">
<q-space />
<q-checkbox
v-model="filters.hide_inactive_users"
color="accent"
:label="$t('employee_management.filter.hide_terminated')"
/>
</div>
</template>
<template #header="props">
@ -125,7 +173,10 @@
</template>
<template #body-cell="scope">
<q-td :props="scope">
<q-td
:props="scope"
@click="employee_store.openAddModifyDialog(scope.row.email)"
>
<transition
appear
enter-active-class="animated fadeInUp slow"
@ -134,7 +185,7 @@
>
<div
:key="scope.rowIndex + (timesheet_store.pay_period?.pay_period_no ?? 0)"
class="rounded-5"
class="rounded-5 cursor-pointer"
style="font-size: 1.2em;"
:style="`animation-delay: ${scope.rowIndex / 30}s;`"
>

View File

@ -40,8 +40,8 @@
</script>
<template>
<div class="row full-width items-start content-start">
<div class="column col-3">
<div class="row full-width items-start content-start overflow-hidden-y">
<div class="column col-3 overflow-hidden-y">
<span class="text-uppercase text-weight-medium q-mx-sm">
{{ $t('employee_management.module_access.by_role') }}
</span>

View File

@ -10,6 +10,16 @@
const is_first_day_picker_open = ref(false);
const is_last_day_picker_open = ref(false);
const company_options = [
{ label: 'Targo', value: 'Targo' },
{ label: 'Solucom', value: 'Solucom' },
]
const supervisor_options = computed(() => {
const supervisors = employee_store.employee_list.filter(employee => employee.is_supervisor === true);
return supervisors.map(supervisor => supervisor.first_name + ' ' + supervisor.last_name);
})
const setLastWorkDay = (date: string | number | null) => {
if (typeof date === 'string' && date.length > 0) {
employee_store.employee.last_work_day = date;
@ -21,9 +31,28 @@
<template>
<div>
<q-form>
<div class="row flex-center">
<transition
enter-active-class="animated pulse fast"
mode="out-in"
>
<q-checkbox
v-model="employee_store.employee.is_supervisor"
:key="employee_store.employee.is_supervisor ? '1' : '0'"
dense
left-label
:label="$t('employee_list.table.is_supervisor')"
size="lg"
color="accent"
class="col-auto text-uppercase q-py-xs q-px-lg q-ma-xs rounded-25"
:class="employee_store.employee.is_supervisor ? 'bg-accent text-white text-weight-bold' : ''"
/>
</transition>
</div>
<div
class="q-ma-xs"
:class="$q.screen.lt.md ? 'column' : 'row'"
:class="$q.screen.lt.sm ? 'column' : 'row'"
>
<q-input
v-model="employee_store.employee.first_name"
@ -62,7 +91,7 @@
<div
class="q-ma-xs"
:class="$q.screen.lt.md ? 'column' : 'row'"
:class="$q.screen.lt.sm ? 'column' : 'row'"
>
<q-input
v-model="employee_store.employee.email"
@ -102,7 +131,7 @@
<div
class="q-ma-xs"
:class="$q.screen.lt.md ? 'column' : 'row'"
:class="$q.screen.lt.sm ? 'column' : 'row'"
>
<q-input
v-model="employee_store.employee.job_title"
@ -121,10 +150,12 @@
</template>
</q-input>
<q-input
<q-select
v-model="employee_store.employee.company_name"
color="accent"
:options="company_options"
stack-label
emit-value
label-slot
class="col q-mx-md"
>
@ -136,18 +167,20 @@
{{ $t('profile.employee.company') }}
</span>
</template>
</q-input>
</q-select>
</div>
<div
class="q-ma-xs"
:class="$q.screen.lt.md ? 'column' : 'row'"
:class="$q.screen.lt.sm ? 'column' : 'row'"
>
<q-input
<q-select
v-model="employee_store.employee.supervisor_full_name"
color="accent"
stack-label
label-slot
:options="supervisor_options"
options-selected-class="text-white text-bold bg-accent"
class="col q-mx-md"
>
<template #label>
@ -158,7 +191,7 @@
{{ $t('profile.employee.supervisor') }}
</span>
</template>
</q-input>
</q-select>
<q-input
v-if="employee_store.management_mode === 'modify_employee'"

View File

@ -68,7 +68,7 @@
</div>
<div class="col column q-pa-md no-wrap">
<div class="col column q-pa-md no-wrap scroll">
<div class="col">
<transition
:enter-active-class="'animated ' + transition_in_animation"
@ -85,42 +85,31 @@
<AddModifyDialogAccess v-else />
</transition>
</div>
<div class="row col-auto q-py-sm">
<q-btn
v-if="current_step === 'access'"
flat
size="lg"
color="accent"
icon="arrow_back"
:label="$t('employee_management.details_label')"
@click="getNextMenu('fadeInLeft', 'fadeOutRight', 'form')"
/>
<q-space />
<q-btn
v-if="current_step === 'form'"
flat
size="lg"
color="accent"
icon-right="arrow_forward"
:label="$t('employee_management.access_label')"
@click="getNextMenu('fadeInRight', 'fadeOutLeft', 'access')"
/>
</div>
</div>
<!-- <div class="col-auto self-center q-pa-xs">
<q-btn
push
<div class="row col-auto">
<q-btn
v-if="current_step === 'access'"
flat
size="lg"
color="accent"
:label="$t('shared.label.save')"
class="q-py-xs"
icon="arrow_back"
:label="$t('employee_management.details_label')"
@click="getNextMenu('fadeInLeft', 'fadeOutRight', 'form')"
/>
</div> -->
<q-space />
<q-btn
v-if="current_step === 'form'"
flat
size="lg"
color="accent"
icon-right="arrow_forward"
:label="$t('employee_management.access_label')"
@click="getNextMenu('fadeInRight', 'fadeOutLeft', 'access')"
/>
</div>
<q-btn
square
@ -129,7 +118,7 @@
class="col-auto q-py-sm shadow-up-5"
@click="employee_store.createOrUpdateEmployee(employee_store.employee)"
/>
<q-inner-loading :showing="employee_store.is_loading" />
<!-- <q-inner-loading :showing="employee_store.is_loading" /> -->
</div>
</q-dialog>
</template>

View File

@ -17,6 +17,7 @@ export class EmployeeProfile {
external_payroll_id: number;
residence: string;
birth_date: string;
is_supervisor: boolean;
user_module_access: ModuleAccessName[];
constructor() {
@ -31,47 +32,66 @@ export class EmployeeProfile {
this.last_work_day = null;
this.residence = '';
this.birth_date = '';
this.is_supervisor = false;
this.external_payroll_id = -1;
this.user_module_access = ['dashboard',];
}
}
export interface EmployeeListFilters {
search_bar_string: string;
hide_inactive_users: boolean;
};
export const employee_list_columns: QTableColumn<EmployeeProfile>[] = [
{
name: 'first_name',
label: 'timesheet_approvals.table.full_name',
field: 'first_name',
align: 'left'
align: 'left',
sortable: true,
},
{
name: 'last_name',
label: 'employee_list.table.last_name',
field: 'last_name',
align: 'left'
align: 'left',
sortable: true,
},
{
name: 'email',
label: 'employee_list.table.email',
field: 'email',
align: 'left'
align: 'left',
sortable: true,
},
{
name: 'supervisor_full_name',
label: 'employee_list.table.supervisor',
field: 'supervisor_full_name',
align: 'left'
align: 'left',
sortable: true,
},
{
name: 'company_name',
label: 'employee_list.table.company',
field: 'company_name',
align: 'left'
align: 'left',
sortable: true,
},
{
name: 'job_title',
label: 'employee_list.table.role',
field: 'job_title',
align: 'left'
align: 'left',
sortable: true,
},
{
name: 'last_work_day',
label: 'status',
field: 'last_work_day',
align: 'center',
sortable: true,
},
];