Merge pull request 'dev/nicolas/employee-list' (#2) from dev/nicolas/employee-list into main
Reviewed-on: Targo/targo_frontend#2
This commit is contained in:
commit
52bc26052e
Binary file not shown.
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 13 KiB |
|
|
@ -14,7 +14,7 @@ declare module 'vue' {
|
||||||
// good idea to move this instance creation inside of the
|
// good idea to move this instance creation inside of the
|
||||||
// "export default () => {}" function below (which runs individually
|
// "export default () => {}" function below (which runs individually
|
||||||
// for each client)
|
// for each client)
|
||||||
const api = axios.create({ baseURL: import.meta.env.VITE_TARGO_BACKEND_URL });
|
const api = axios.create({ baseURL: import.meta.env.VITE_TARGO_BACKEND_AUTH_URL });
|
||||||
|
|
||||||
export default defineBoot(({ app }) => {
|
export default defineBoot(({ app }) => {
|
||||||
// for use inside Vue files (Options API) through this.$axios and this.$api
|
// for use inside Vue files (Options API) through this.$axios and this.$api
|
||||||
|
|
|
||||||
|
|
@ -19,3 +19,7 @@
|
||||||
.bg-authentik-orange {
|
.bg-authentik-orange {
|
||||||
background: #fd4b2d !important;
|
background: #fd4b2d !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.q-table tbody tr:hover {
|
||||||
|
background: #00ff260c;
|
||||||
|
}
|
||||||
|
|
@ -140,20 +140,16 @@ export default {
|
||||||
card_4: 'Customers',
|
card_4: 'Customers',
|
||||||
},
|
},
|
||||||
usersListPage: {
|
usersListPage: {
|
||||||
tableHeader: 'Users list',
|
tableHeader: 'Employee list',
|
||||||
search_input: 'Search',
|
searchInput: 'Search',
|
||||||
tableCol_1: 'Status',
|
userListFirstName: 'First name',
|
||||||
tableCol_2: 'First name',
|
userListLastName: 'Last name',
|
||||||
tableCol_3: 'Last name',
|
userListEmail: 'Email',
|
||||||
tableCol_4: 'Email',
|
userListPhone: 'Phone number',
|
||||||
tableCol_5: 'Phone number',
|
userListRole: 'Role',
|
||||||
tableCol_6: 'User type',
|
userListSupervisor: 'Supervisor',
|
||||||
tableCol_7: 'Role',
|
userListCompany: 'Company',
|
||||||
tableCol_8: 'Created by',
|
addButton: 'Add Employee',
|
||||||
tableCol_9: 'Supervisor',
|
|
||||||
activeStatus: 'Active',
|
|
||||||
unActiveStatus: 'Unactive',
|
|
||||||
addButton: 'Click here to add a new user',
|
|
||||||
customer: 'Customer',
|
customer: 'Customer',
|
||||||
dealer: 'Dealer',
|
dealer: 'Dealer',
|
||||||
employee: 'Employee',
|
employee: 'Employee',
|
||||||
|
|
@ -161,6 +157,9 @@ export default {
|
||||||
admin: 'Administrator',
|
admin: 'Administrator',
|
||||||
support: 'Support',
|
support: 'Support',
|
||||||
},
|
},
|
||||||
|
shared:{
|
||||||
|
searchBar: 'Search',
|
||||||
|
},
|
||||||
editUserPage: {
|
editUserPage: {
|
||||||
title: 'Edit Account',
|
title: 'Edit Account',
|
||||||
passwordTitle: 'Reset Password',
|
passwordTitle: 'Reset Password',
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,9 @@ export default {
|
||||||
submit: 'Envoyer',
|
submit: 'Envoyer',
|
||||||
cancel: 'Annuler',
|
cancel: 'Annuler',
|
||||||
},
|
},
|
||||||
|
shared:{
|
||||||
|
searchBar: 'Rechercher',
|
||||||
|
},
|
||||||
shiftColumns: {
|
shiftColumns: {
|
||||||
title: 'Quarts de travail',
|
title: 'Quarts de travail',
|
||||||
column_1: 'Type',
|
column_1: 'Type',
|
||||||
|
|
@ -372,20 +375,16 @@ export default {
|
||||||
unlockToolTip: 'Déverrouiller la semaine',
|
unlockToolTip: 'Déverrouiller la semaine',
|
||||||
},
|
},
|
||||||
usersListPage: {
|
usersListPage: {
|
||||||
tableHeader: 'Liste des utilisateurs',
|
tableHeader: 'Liste d’employées',
|
||||||
search_input: 'Rechercher',
|
searchInput: 'rechercher',
|
||||||
tableCol_1: 'État',
|
userListFirstName: 'prénom',
|
||||||
tableCol_2: 'Prénom',
|
userListLastName: 'nom de famille',
|
||||||
tableCol_3: 'Nom de famille',
|
userListEmail: 'courriel',
|
||||||
tableCol_4: 'Email',
|
userListPhone: '# téléphone',
|
||||||
tableCol_5: 'Numéro de téléphone',
|
userListRole: 'rôle',
|
||||||
tableCol_6: 'Type d’utilisateur',
|
userListSupervisor: 'superviseur',
|
||||||
tableCol_7: 'Role',
|
userListCompany: 'Compagnie',
|
||||||
tableCol_8: 'Créé par',
|
addButton: 'Ajouter employé',
|
||||||
tableCol_9: 'Superviseur',
|
|
||||||
activeStatus: 'Actif',
|
|
||||||
unActiveStatus: 'Inactif',
|
|
||||||
addButton: 'Cliquez ici pour ajouter un nouvel utilisateur',
|
|
||||||
customer: 'Client',
|
customer: 'Client',
|
||||||
dealer: 'Marchand',
|
dealer: 'Marchand',
|
||||||
employee: 'Employé',
|
employee: 'Employé',
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
<HeaderBar />
|
<HeaderBar />
|
||||||
<RightDrawer />
|
<RightDrawer />
|
||||||
<q-page-container>
|
<q-page-container>
|
||||||
<router-view class="q-pa-sm" />
|
<router-view class="q-pa-sm bg-secondary" />
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
<FooterBar />
|
<FooterBar />
|
||||||
</q-layout>
|
</q-layout>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useEmployeeStore } from 'src/stores/employee-store';
|
||||||
|
|
||||||
|
const employeeStore = useEmployeeStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-dialog v-model="employeeStore.isShowingEmployeeAddModifyWindow">
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
LOL
|
||||||
|
</q-card-section>
|
||||||
|
<q-inner-loading :showing="employeeStore.isLoadingEmployeeProfile"/>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
/* eslint-disable */
|
||||||
|
import { useEmployeeStore } from 'src/stores/employee-store';
|
||||||
|
import type { EmployeeListTableItem } from 'src/modules/employee-list/types/employee-list-table-interface';
|
||||||
|
import { useEmployeeListApi } from 'src/modules/employee-list/composables/use-employee-api';
|
||||||
|
|
||||||
|
const employeeStore = useEmployeeStore();
|
||||||
|
const employeeListApi = useEmployeeListApi();
|
||||||
|
|
||||||
|
const getEmployeeAvatar = (first_name: string, last_name: string) => {
|
||||||
|
// add logic here to see if user has an avatar image and return that instead of initials
|
||||||
|
return first_name.charAt(0) + last_name.charAt(0);
|
||||||
|
};
|
||||||
|
const onProfileCardClick = (email: string) => {
|
||||||
|
employeeStore.isShowingEmployeeAddModifyWindow = true;
|
||||||
|
console.log("clicked profile!");
|
||||||
|
employeeListApi.getEmployeeDetails(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
row: EmployeeListTableItem
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-card
|
||||||
|
v-ripple
|
||||||
|
class="rounded-15 bg-white col-xs-6 col-sm-4 col-md-3 col-lg-2 column no-wrap cursor-pointer"
|
||||||
|
style="max-width: 230px;"
|
||||||
|
@click="onProfileCardClick(props.row.email)"
|
||||||
|
>
|
||||||
|
<q-card-section class="text-center col-5">
|
||||||
|
<q-avatar color="primary" size="8em">
|
||||||
|
<img src="src/assets/targo-default-avatar.png" alt="employee avatar" class="q-pa-xs">
|
||||||
|
</q-avatar>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section class="text-center text-h6 text-primary text-weight-medium text-uppercase q-pb-none col-2 content-end" style="line-height: 0.7em;">
|
||||||
|
<div class="ellipsis">
|
||||||
|
{{ props.row.first_name }} {{ props.row.last_name }}
|
||||||
|
<q-separator color="primary" />
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section class="text-caption text-grey-8 text-body2 text-uppercase q-pt-none text-center col content-start" style="min-height: 5em;">
|
||||||
|
<div class=" ellipsis-2-lines">
|
||||||
|
{{ props.row.job_title }}
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section class="bg-primary text-white text-caption text-center q-py-none col-2 content-center ">
|
||||||
|
<div>
|
||||||
|
{{ props.row.email }}
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onMounted, ref } from 'vue';
|
||||||
|
import { useEmployeeListApi } from 'src/modules/employee-list/composables/use-employee-api';
|
||||||
|
import { useEmployeeStore } from 'src/stores/employee-store';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import SupervisorCrewTableItem from './supervisor-crew-table-item.vue';
|
||||||
|
|
||||||
|
import type { EmployeeListTableItem } from '../../types/employee-list-table-interface';
|
||||||
|
import type { QTableColumn } from 'quasar';
|
||||||
|
|
||||||
|
const employeeListApi = useEmployeeListApi();
|
||||||
|
const employeeStore = useEmployeeStore();
|
||||||
|
const isLoadingList = ref<boolean>(true);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const filter = ref("");
|
||||||
|
const isGridMode = ref(true);
|
||||||
|
|
||||||
|
const employeeListColumns = computed((): QTableColumn<EmployeeListTableItem>[] => [
|
||||||
|
{name: 'first_name', label: t('usersListPage.userListFirstName'), field: 'first_name'},
|
||||||
|
{name: 'last_name', label: t('usersListPage.userListLastName'), field: 'last_name', align: 'left'},
|
||||||
|
{name: 'email', label: t('usersListPage.userListEmail'), field: 'email', align:'center'},
|
||||||
|
{name: 'supervisor_full_name', label: t('usersListPage.userListSupervisor'), field: 'supervisor_full_name', align: 'left'},
|
||||||
|
{name: 'company_name', label: t('usersListPage.userListCompany'), field: 'company_name'},
|
||||||
|
{name: 'job_title', label: t('usersListPage.userListRole'), field: 'job_title'},
|
||||||
|
]);
|
||||||
|
|
||||||
|
onMounted( () => {
|
||||||
|
employeeListApi.getEmployeeList();
|
||||||
|
isLoadingList.value = false;
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="q-pa-lg col">
|
||||||
|
<q-table
|
||||||
|
title=" "
|
||||||
|
:rows="employeeStore.employeeList"
|
||||||
|
:columns="employeeListColumns"
|
||||||
|
row-key="name"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
:filter="filter"
|
||||||
|
class="q-pa-md"
|
||||||
|
table-header-class="text-primary text-uppercase"
|
||||||
|
card-container-class="justify-center q-gutter-md"
|
||||||
|
:grid="isGridMode"
|
||||||
|
:loading="isLoadingList"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
>
|
||||||
|
<template v-slot:item="props">
|
||||||
|
<SupervisorCrewTableItem :row="props.row"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:top>
|
||||||
|
<q-btn push icon="person_add" color="primary" :label="$t('usersListPage.addButton')"/>
|
||||||
|
<q-space />
|
||||||
|
<div class="row q-mb-lg">
|
||||||
|
<q-btn-toggle push class="q-mr-md" color="white" text-color="primary" toggle-color="primary" v-model="isGridMode"
|
||||||
|
:options="[
|
||||||
|
{icon: 'grid_view', value: true},
|
||||||
|
{icon: 'view_list', value: false},
|
||||||
|
]"/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
v-model="filter"
|
||||||
|
:label="$t('shared.searchBar')"
|
||||||
|
label-color="primary" bg-color="white" color="primary"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="search" color="primary"/>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
18
src/modules/employee-list/composables/use-employee-api.ts
Normal file
18
src/modules/employee-list/composables/use-employee-api.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { useEmployeeStore } from "src/stores/employee-store";
|
||||||
|
|
||||||
|
export const useEmployeeListApi = () => {
|
||||||
|
const employeeListStore = useEmployeeStore();
|
||||||
|
|
||||||
|
const getEmployeeList = (): Promise<void> => {
|
||||||
|
return employeeListStore.getEmployeeList();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEmployeeDetails = (email: string): Promise<void> => {
|
||||||
|
return employeeListStore.getEmployeeDetails(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getEmployeeList,
|
||||||
|
getEmployeeDetails,
|
||||||
|
};
|
||||||
|
};
|
||||||
14
src/modules/employee-list/pages/supervisor-crew-page.vue
Normal file
14
src/modules/employee-list/pages/supervisor-crew-page.vue
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import SupervisorCrewTable from '../components/supervisor/supervisor-crew-table.vue';
|
||||||
|
import EmployeeListAddModifyDialog from '../components/employee/employee-list-add-modify-dialog.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-page>
|
||||||
|
<EmployeeListAddModifyDialog />
|
||||||
|
<div class="text-h4 row justify-center q-py-sm q-mt-lg text-uppercase text-weight-bolder text-primary">
|
||||||
|
{{ $t('usersListPage.tableHeader') }}
|
||||||
|
</div>
|
||||||
|
<SupervisorCrewTable />
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
17
src/modules/employee-list/services/services-employee-list.ts
Normal file
17
src/modules/employee-list/services/services-employee-list.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
// /* eslint-disable */
|
||||||
|
import { api } from 'src/boot/axios';
|
||||||
|
import type { EmployeeListTableItem } from '../types/employee-list-table-interface';
|
||||||
|
import type { EmployeeProfile } from '../types/employee-profile-interface';
|
||||||
|
|
||||||
|
|
||||||
|
export const EmployeeListService = {
|
||||||
|
getEmployeeList: async (): Promise<EmployeeListTableItem[]> => {
|
||||||
|
const response = await api.get<EmployeeListTableItem[]>('/employees/employee-list')
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
getEmployeeDetails: async (email: string): Promise<EmployeeProfile> => {
|
||||||
|
const response = await api.get<EmployeeProfile>('employees/profile/' + email);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
export interface EmployeeListTableItem {
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
email: string;
|
||||||
|
supervisor_full_name: string | null;
|
||||||
|
company_name: number;
|
||||||
|
job_title: string;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
export interface EmployeeProfile {
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
supervisor_full_name: string;
|
||||||
|
company_name: number;
|
||||||
|
job_title: string;
|
||||||
|
email: string;
|
||||||
|
phone_number: number;
|
||||||
|
first_work_day: string;
|
||||||
|
last_work_day: string;
|
||||||
|
residence: string;
|
||||||
|
}
|
||||||
|
|
@ -70,7 +70,7 @@
|
||||||
<div class="text-primary text-h5 text-weight-bolder ellipsis">{{ props.row.employee_name }}</div>
|
<div class="text-primary text-h5 text-weight-bolder ellipsis">{{ props.row.employee_name }}</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<div v-for="col in props.cols.filter(col => col.name !== 'employee_name')" class="q-pa-none q-mx-sm items-center row" :class="{ 'bg-warning': col.name == 'overtime_hours' && col.value as number > 0 }" >
|
<div v-for="col in props.cols.filter(col => col.name !== 'employee_name')" class="q-pa-none q-mx-sm items-center row" :class="{ 'bg-warning': col.name == 'overtime_hours' && col.value as number > 0 }" >
|
||||||
<q-card-section class="text-right text-weight-bolder text-subtitle1 text-primary q-pr-sm q-py-none col-3" style="line-height: 1.2em;">{{ col.value }}</q-card-section>
|
<q-card-section class="text-right text-weight-bolder text-subtitle1 text-primary q-pr-sm q-py-none col-3 ellipsis" style="line-height: 1.2em;">{{ col.value }}</q-card-section>
|
||||||
<q-card-section class="text-weight-bold q-pa-none col-9" >{{ col.label }}</q-card-section>
|
<q-card-section class="text-weight-bold q-pa-none col-9" >{{ col.label }}</q-card-section>
|
||||||
</div>
|
</div>
|
||||||
<q-card-section horizontal class="q-pa-sm q-mt-sm" :class="{ 'bg-primary text-white': props.selected}">
|
<q-card-section horizontal class="q-pa-sm q-mt-sm" :class="{ 'bg-primary text-white': props.selected}">
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-page padding class="q-pa-md row items-center justify-center q-gutter-md">
|
<q-page padding class="q-pa-md row items-center justify-center">
|
||||||
<q-card class="shadow-2 col-9 dark-font">
|
<q-card class="shadow-2 col-9 dark-font">
|
||||||
<q-img src="src/assets/line-truck-1.jpg">
|
<q-img src="src/assets/line-truck-1.jpg">
|
||||||
<div class="absolute-bottom text-h5">
|
<div class="absolute-bottom text-h5">
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,11 @@ const routes: RouteRecordRaw[] = [
|
||||||
name: RouteNames.TIMESHEET_APPROVALS,
|
name: RouteNames.TIMESHEET_APPROVALS,
|
||||||
component: () => import('src/modules/timesheet-approval/pages/timesheet-approval.vue'),
|
component: () => import('src/modules/timesheet-approval/pages/timesheet-approval.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'employees',
|
||||||
|
name: RouteNames.EMPLOYEE_LIST,
|
||||||
|
component: () => import('src/modules/employee-list/pages/supervisor-crew-page.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ const defaultUser: User = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useAuthStore = defineStore('auth', () => {
|
export const useAuthStore = defineStore('auth', () => {
|
||||||
const user = ref (defaultUser);
|
const user = ref(defaultUser);
|
||||||
const authError = ref("");
|
const authError = ref("");
|
||||||
const isAuthorizedUser = computed(() => user.value.role !== 'guest');
|
const isAuthorizedUser = computed(() => user.value.role !== 'guest');
|
||||||
|
|
||||||
|
|
|
||||||
41
src/stores/employee-store.ts
Normal file
41
src/stores/employee-store.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { EmployeeListService } from "src/modules/employee-list/services/services-employee-list";
|
||||||
|
import type { EmployeeProfile } from "src/modules/employee-list/types/employee-profile-interface";
|
||||||
|
import type { EmployeeListTableItem } from "src/modules/employee-list/types/employee-list-table-interface";
|
||||||
|
|
||||||
|
export const useEmployeeStore = defineStore('employee', () => {
|
||||||
|
const employee = ref<EmployeeProfile>();
|
||||||
|
const employeeList = ref<EmployeeListTableItem[]>([]);
|
||||||
|
const isShowingEmployeeAddModifyWindow = ref<boolean>(false);
|
||||||
|
const isLoadingEmployeeProfile = ref(false);
|
||||||
|
const isLoadingEmployeeList = ref(false);
|
||||||
|
|
||||||
|
const getEmployeeList = async () => {
|
||||||
|
isLoadingEmployeeList.value = true;
|
||||||
|
try {
|
||||||
|
const response = await EmployeeListService.getEmployeeList();
|
||||||
|
employeeList.value = response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Ran into an error fetching employee list: ", error);
|
||||||
|
//TODO: trigger an alert window with an error message here!
|
||||||
|
}
|
||||||
|
isLoadingEmployeeList.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEmployeeDetails = async (email: string) => {
|
||||||
|
isLoadingEmployeeProfile.value = true;
|
||||||
|
try {
|
||||||
|
const response = await EmployeeListService.getEmployeeDetails(email);
|
||||||
|
employee.value = response;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('There was an error retrieving employee info: ', error);
|
||||||
|
//TODO: trigger an alert window with an error message here!
|
||||||
|
}
|
||||||
|
isLoadingEmployeeProfile.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
return { employee, employeeList, isShowingEmployeeAddModifyWindow, isLoadingEmployeeList, isLoadingEmployeeProfile, getEmployeeList, getEmployeeDetails };
|
||||||
|
});
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user