feat(employee-list): add error messages to form when modifying employee details, minor UI changes.
This commit is contained in:
parent
ffe671376f
commit
d7b4e150ac
|
|
@ -74,6 +74,15 @@ export default {
|
||||||
active: "active",
|
active: "active",
|
||||||
inactive: "inactive",
|
inactive: "inactive",
|
||||||
},
|
},
|
||||||
|
errors: {
|
||||||
|
first_name_required: "Employee's first name is required",
|
||||||
|
last_name_required: "Employee's last name is required",
|
||||||
|
company_required: "Employee must be assigned to a company",
|
||||||
|
phone_number_required: "Employee's phone number required",
|
||||||
|
hire_date_required: "Employee's first work day must be entered",
|
||||||
|
daily_hours_required: "Provide employee's expected daily hours worked",
|
||||||
|
no_modules_warning: "All modules disabled. This will lock the user out.",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
employee_management: {
|
employee_management: {
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,15 @@ export default {
|
||||||
active: "actif",
|
active: "actif",
|
||||||
inactive: "inactif",
|
inactive: "inactif",
|
||||||
},
|
},
|
||||||
|
errors: {
|
||||||
|
first_name_required: "Vous devez spécifier le prénom",
|
||||||
|
last_name_required: "Vous devez spécifier le nom de famille",
|
||||||
|
company_required: "Vous devez assignerl'employé à une compagnie",
|
||||||
|
phone_number_required: "Vous devez entrer un numéro de téléphone",
|
||||||
|
hire_date_required: "Vous devez entrer une date d'embauche",
|
||||||
|
daily_hours_required: "Spécifiez le nombre d'heures quotidiennes travaillé",
|
||||||
|
no_modules_warning: "Tout les modules sont désactivés. L'utilisateur sera verrouillé hors de l'application.",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
employee_management: {
|
employee_management: {
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,34 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="row full-width items-start content-start overflow-hidden-y">
|
<div class="column full-width items-start content-start overflow-hidden-y">
|
||||||
<div class="col-12 row flex-center q-px-sm q-py-xs no-wrap">
|
<!-- warning when all modules are disabled -->
|
||||||
|
<div class="col-auto row flex-center q-px-lg q-py-xs full-width">
|
||||||
|
<q-slide-transition>
|
||||||
|
<div
|
||||||
|
v-if="employee_store.employee.user_module_access.length === 0"
|
||||||
|
class="row flex-center q-px-md q-py-xs bg-dark"
|
||||||
|
style="border: 2px solid var(--q-warning);"
|
||||||
|
>
|
||||||
|
<q-icon
|
||||||
|
name="las la-exclamation-triangle"
|
||||||
|
color="warning"
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span class="text-warning text-weight-medium q-px-sm">{{ $t('employee_list.errors.no_modules_warning') }}</span>
|
||||||
|
|
||||||
|
<q-icon
|
||||||
|
name="las la-exclamation-triangle"
|
||||||
|
color="warning"
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-slide-transition>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- info line explaining how to customize access -->
|
||||||
|
<div class="col-auto row flex-center q-px-sm q-py-xs no-wrap">
|
||||||
<q-icon
|
<q-icon
|
||||||
name="info_outline"
|
name="info_outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
@ -57,74 +83,14 @@
|
||||||
>{{ $t('employee_management.module_access.usage_description') }}</q-item-label>
|
>{{ $t('employee_management.module_access.usage_description') }}</q-item-label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="column col-3 overflow-hidden-y">
|
<div
|
||||||
<span class="text-uppercase text-weight-medium q-mx-sm">
|
class="col"
|
||||||
{{ $t('employee_management.module_access.by_role') }}
|
:class="$q.platform.is.mobile ? 'column' : 'row'"
|
||||||
</span>
|
>
|
||||||
|
<!-- column to attribute access by roles -->
|
||||||
<q-separator
|
<div class="column col-4 overflow-hidden-y q-pr-sm">
|
||||||
size="2px"
|
|
||||||
color="accent"
|
|
||||||
class="q-mx-sm"
|
|
||||||
style="transform: translateY(-4px);"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<q-item
|
|
||||||
clickable
|
|
||||||
class="shadow-2 rounded-5 q-ma-sm bg-dark"
|
|
||||||
@click="applyAccessPreset('admin')"
|
|
||||||
@mouseover="preset_preview = 'admin'"
|
|
||||||
@mouseleave="preset_preview = undefined"
|
|
||||||
>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label class="text-uppercase text-weight-bold">
|
|
||||||
{{ $t('employee_management.module_access.preset_admin') }}
|
|
||||||
</q-item-label>
|
|
||||||
|
|
||||||
<q-item-label caption>
|
|
||||||
{{ $t('employee_management.module_access.admin_description') }}
|
|
||||||
</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
|
|
||||||
<q-item
|
|
||||||
clickable
|
|
||||||
class="shadow-2 rounded-5 q-ma-sm bg-dark"
|
|
||||||
@click="applyAccessPreset('employee')"
|
|
||||||
@mouseover="preset_preview = 'employee'"
|
|
||||||
@mouseleave="preset_preview = undefined"
|
|
||||||
>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label class="text-uppercase text-weight-bold">
|
|
||||||
{{ $t('employee_management.module_access.preset_employee') }}
|
|
||||||
</q-item-label>
|
|
||||||
|
|
||||||
<q-item-label caption>
|
|
||||||
{{ $t('employee_management.module_access.employee_description') }}
|
|
||||||
</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
|
|
||||||
<q-btn
|
|
||||||
flat
|
|
||||||
color="negative"
|
|
||||||
icon="clear"
|
|
||||||
size="md"
|
|
||||||
align="left"
|
|
||||||
:label="$t('employee_management.module_access.uncheck_all')"
|
|
||||||
class="q-ma-sm q-px-xs rounded-5"
|
|
||||||
@click="applyAccessPreset('none')"
|
|
||||||
@mouseover="preset_preview = 'none'"
|
|
||||||
@mouseleave="preset_preview = undefined"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-xs-0 col-sm-1"></div>
|
|
||||||
|
|
||||||
<div class="row col items-start content-start">
|
|
||||||
<div class="col-12 q-mb-xs">
|
|
||||||
<span class="text-uppercase text-weight-medium q-mx-sm">
|
<span class="text-uppercase text-weight-medium q-mx-sm">
|
||||||
{{ $t('employee_management.module_access.by_module') }}
|
{{ $t('employee_management.module_access.by_role') }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<q-separator
|
<q-separator
|
||||||
|
|
@ -133,31 +99,95 @@
|
||||||
class="q-mx-sm"
|
class="q-mx-sm"
|
||||||
style="transform: translateY(-4px);"
|
style="transform: translateY(-4px);"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div
|
<q-item
|
||||||
v-for="option in employee_access_options"
|
clickable
|
||||||
:key="option.label"
|
class="shadow-2 rounded-5 q-ma-sm bg-dark"
|
||||||
class="col-lg-6 col-sm-12 col-xs-12 q-pa-xs"
|
@click="applyAccessPreset('admin')"
|
||||||
>
|
@mouseover="preset_preview = 'admin'"
|
||||||
<div
|
@mouseleave="preset_preview = undefined"
|
||||||
class="row full-width cursor-pointer flex-center q-pa-sm rounded-5 no-wrap shadow-5"
|
|
||||||
:class="preset_preview !== undefined ? getPreviewBackgroundColor(option.value) : getBackgroundColor(option.value)"
|
|
||||||
@click="toggleInSelected(option.value)"
|
|
||||||
>
|
>
|
||||||
<q-icon
|
<q-item-section>
|
||||||
:name="getEmployeeAccessOptionIcon(option.value)"
|
<q-item-label class="text-uppercase text-weight-bold">
|
||||||
size="sm"
|
{{ $t('employee_management.module_access.preset_admin') }}
|
||||||
class="q-mr-sm"
|
</q-item-label>
|
||||||
/>
|
|
||||||
<span class="text-uppercase text-weight-bold non-selectable">
|
<q-item-label caption>
|
||||||
{{ $t('employee_management.module_access.' + option.value) }}
|
{{ $t('employee_management.module_access.admin_description') }}
|
||||||
|
</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
class="shadow-2 rounded-5 q-ma-sm bg-dark"
|
||||||
|
@click="applyAccessPreset('employee')"
|
||||||
|
@mouseover="preset_preview = 'employee'"
|
||||||
|
@mouseleave="preset_preview = undefined"
|
||||||
|
>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label class="text-uppercase text-weight-bold">
|
||||||
|
{{ $t('employee_management.module_access.preset_employee') }}
|
||||||
|
</q-item-label>
|
||||||
|
|
||||||
|
<q-item-label caption>
|
||||||
|
{{ $t('employee_management.module_access.employee_description') }}
|
||||||
|
</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="negative"
|
||||||
|
icon="clear"
|
||||||
|
size="md"
|
||||||
|
align="left"
|
||||||
|
:label="$t('employee_management.module_access.uncheck_all')"
|
||||||
|
class="q-ma-sm q-px-xs rounded-5"
|
||||||
|
@click="applyAccessPreset('none')"
|
||||||
|
@mouseover="preset_preview = 'none'"
|
||||||
|
@mouseleave="preset_preview = undefined"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row col items-start content-start q-pl-sm">
|
||||||
|
<div class="col-12 q-mb-xs">
|
||||||
|
<span class="text-uppercase text-weight-medium q-mx-sm">
|
||||||
|
{{ $t('employee_management.module_access.by_module') }}
|
||||||
</span>
|
</span>
|
||||||
<q-space />
|
|
||||||
<q-icon
|
<q-separator
|
||||||
:name="employee_store.employee.user_module_access.includes(option.value) ? 'check' : ''"
|
size="2px"
|
||||||
size="sm"
|
color="accent"
|
||||||
|
class="q-mx-sm"
|
||||||
|
style="transform: translateY(-4px);"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="option in employee_access_options"
|
||||||
|
:key="option.label"
|
||||||
|
class="col-lg-6 col-sm-12 col-xs-12 q-pa-xs"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="row full-width cursor-pointer flex-center q-pa-sm rounded-5 no-wrap shadow-5"
|
||||||
|
:class="preset_preview !== undefined ? getPreviewBackgroundColor(option.value) : getBackgroundColor(option.value)"
|
||||||
|
@click="toggleInSelected(option.value)"
|
||||||
|
>
|
||||||
|
<q-icon
|
||||||
|
:name="getEmployeeAccessOptionIcon(option.value)"
|
||||||
|
size="sm"
|
||||||
|
class="q-mr-sm"
|
||||||
|
/>
|
||||||
|
<span class="text-uppercase text-weight-bold non-selectable">
|
||||||
|
{{ $t('employee_management.module_access.' + option.value) }}
|
||||||
|
</span>
|
||||||
|
<q-space />
|
||||||
|
<q-icon
|
||||||
|
:name="employee_store.employee.user_module_access.includes(option.value) ? 'check' : ''"
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,9 @@
|
||||||
color="accent"
|
color="accent"
|
||||||
stack-label
|
stack-label
|
||||||
label-slot
|
label-slot
|
||||||
class="col q-px-sm q-py-xs"
|
no-error-icon
|
||||||
|
hide-bottom-space
|
||||||
|
class="col q-mx-sm q-my-xs rounded-5 shadow-12"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span
|
<span
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,11 @@
|
||||||
color="accent"
|
color="accent"
|
||||||
stack-label
|
stack-label
|
||||||
label-slot
|
label-slot
|
||||||
|
lazy-rules
|
||||||
|
no-error-icon
|
||||||
|
hide-bottom-space
|
||||||
options-selected-class="text-white text-bold bg-accent"
|
options-selected-class="text-white text-bold bg-accent"
|
||||||
class="col q-px-sm q-py-xs"
|
class="col q-mx-sm q-my-xs bg-dark rounded-5 shadow-12"
|
||||||
popup-content-class="text-uppercase text-weight-medium rounded-5"
|
popup-content-class="text-uppercase text-weight-medium rounded-5"
|
||||||
popup-content-style="border: 2px solid var(--q-accent)"
|
popup-content-style="border: 2px solid var(--q-accent)"
|
||||||
menu-anchor="bottom middle"
|
menu-anchor="bottom middle"
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,14 @@
|
||||||
|
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useEmployeeStore } from 'src/stores/employee-store';
|
import { useEmployeeStore } from 'src/stores/employee-store';
|
||||||
|
import { type QuasarRules, useEmployeeProfileRules, company_options } from 'src/modules/employee-list/employee-list-utils';
|
||||||
|
|
||||||
const employee_store = useEmployeeStore();
|
const employee_store = useEmployeeStore();
|
||||||
const last_work_day = computed(() => employee_store.employee.last_work_day ?? '---');
|
const last_work_day = computed(() => employee_store.employee.last_work_day ?? '---');
|
||||||
const is_first_day_picker_open = ref(false);
|
const is_first_day_picker_open = ref(false);
|
||||||
const is_last_day_picker_open = ref(false);
|
const is_last_day_picker_open = ref(false);
|
||||||
|
|
||||||
const company_options = [
|
const form_rules = useEmployeeProfileRules();
|
||||||
{ label: 'Targo', value: 'Targo' },
|
|
||||||
{ label: 'Solucom', value: 'Solucom' },
|
|
||||||
]
|
|
||||||
|
|
||||||
const supervisor_options = computed(() => {
|
const supervisor_options = computed(() => {
|
||||||
const supervisors = employee_store.employee_list.filter(employee => employee.is_supervisor === true && employee.last_work_day === null);
|
const supervisors = employee_store.employee_list.filter(employee => employee.is_supervisor === true && employee.last_work_day === null);
|
||||||
|
|
@ -60,11 +58,13 @@
|
||||||
<AddModifyDialogFormInput
|
<AddModifyDialogFormInput
|
||||||
v-model="employee_store.employee.first_name"
|
v-model="employee_store.employee.first_name"
|
||||||
:label="$t('profile.personal.first_name')"
|
:label="$t('profile.personal.first_name')"
|
||||||
|
:rules="[(value: string, rules: QuasarRules) => form_rules.isNotEmpty(value, rules, $t('employee_list.errors.first_name_required'))]"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AddModifyDialogFormInput
|
<AddModifyDialogFormInput
|
||||||
v-model="employee_store.employee.last_name"
|
v-model="employee_store.employee.last_name"
|
||||||
:label="$t('profile.personal.last_name')"
|
:label="$t('profile.personal.last_name')"
|
||||||
|
:rules="[(value: string, rules: QuasarRules) => form_rules.isNotEmpty(value, rules, $t('employee_list.errors.last_name_required'))]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -80,6 +80,8 @@
|
||||||
<AddModifyDialogFormInput
|
<AddModifyDialogFormInput
|
||||||
v-model="employee_store.employee.phone_number"
|
v-model="employee_store.employee.phone_number"
|
||||||
:label="$t('profile.personal.phone_number')"
|
:label="$t('profile.personal.phone_number')"
|
||||||
|
mask="(###) ### - ####"
|
||||||
|
:rules="[(value: string, rules: QuasarRules) => form_rules.isNotEmpty(value, rules, $t('employee_list.errors.phone_number_required'))]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -96,6 +98,7 @@
|
||||||
v-model="employee_store.employee.company_name"
|
v-model="employee_store.employee.company_name"
|
||||||
:options="company_options"
|
:options="company_options"
|
||||||
:label="$t('profile.employee.company')"
|
:label="$t('profile.employee.company')"
|
||||||
|
:rules="[(value: string, rules: QuasarRules) => form_rules.isNotEmpty(value, rules, $t('employee_list.errors.company_required'))]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -123,6 +126,7 @@
|
||||||
<AddModifyDialogFormInput
|
<AddModifyDialogFormInput
|
||||||
v-model="employee_store.employee.daily_expected_hours"
|
v-model="employee_store.employee.daily_expected_hours"
|
||||||
:label="$t('employee_list.table.expected_daily_hours')"
|
:label="$t('employee_list.table.expected_daily_hours')"
|
||||||
|
:rules="[(value: string, rules: QuasarRules) => form_rules.isNotEmpty(value, rules, $t('employee_list.errors.daily_hours_required'))]"
|
||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -133,7 +137,10 @@
|
||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div v-else class="col q-px-sm"></div>
|
<div
|
||||||
|
v-else
|
||||||
|
class="col q-px-sm"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
@ -165,6 +172,7 @@
|
||||||
v-model:is-date-picker-open="is_first_day_picker_open"
|
v-model:is-date-picker-open="is_first_day_picker_open"
|
||||||
reqires-date-picker
|
reqires-date-picker
|
||||||
:label="$t('profile.employee.hired_date')"
|
:label="$t('profile.employee.hired_date')"
|
||||||
|
:rules="[(value: string, rules: QuasarRules) => form_rules.isNotEmpty(value, rules, $t('employee_list.errors.hire_date_required'))]"
|
||||||
mask="####-##-##"
|
mask="####-##-##"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -179,4 +187,24 @@
|
||||||
</div>
|
</div>
|
||||||
</q-form>
|
</q-form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
:deep(.q-field--error .q-field__bottom) {
|
||||||
|
color: white;
|
||||||
|
font-weight: 500;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border-radius: 0 0 5px 5px;
|
||||||
|
padding-top: 0;
|
||||||
|
align-items: center;
|
||||||
|
background-color: var(--q-negative);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.row > .col) {
|
||||||
|
height: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.q-field--outlined.q-field--highlighted .q-field__control::after) {
|
||||||
|
border-radius: 5px 5px 0 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
17
src/modules/employee-list/employee-list-utils.ts
Normal file
17
src/modules/employee-list/employee-list-utils.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import type { EmbeddedValidationRule, EmbeddedValidationRuleFn } from "quasar";
|
||||||
|
|
||||||
|
export type QuasarRules = Record<EmbeddedValidationRule, EmbeddedValidationRuleFn>;
|
||||||
|
type EmployeeProfileValidationRule<T> = EmbeddedValidationRule | ((value: T, rules: QuasarRules, error_message: string) => boolean | string | Promise<boolean | string>);
|
||||||
|
|
||||||
|
export const useEmployeeProfileRules = () => {
|
||||||
|
const isNotEmpty: EmployeeProfileValidationRule<unknown> = (value, _rules, error_message) => (value !== undefined && value !== null && value !== '') || error_message;
|
||||||
|
|
||||||
|
return {
|
||||||
|
isNotEmpty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const company_options = [
|
||||||
|
{ label: 'Targo', value: 'Targo' },
|
||||||
|
{ label: 'Solucom', value: 'Solucom' },
|
||||||
|
]
|
||||||
Loading…
Reference in New Issue
Block a user