312 lines
8.0 KiB
Vue
312 lines
8.0 KiB
Vue
<template>
|
|
<div class="card p-3 border-radius-xl bg-white" data-animation="FadeIn">
|
|
<h5 class="font-weight-bolder mb-0">Nouvel Employé</h5>
|
|
<p class="mb-0 text-sm">Informations de l'employé</p>
|
|
|
|
<div class="multisteps-form__content">
|
|
<!-- Nom & Prénom -->
|
|
<div class="row mt-3">
|
|
<div class="col-12 col-sm-6">
|
|
<label class="form-label"
|
|
>Prénom <span class="text-danger">*</span></label
|
|
>
|
|
<soft-input
|
|
:value="form.first_name"
|
|
class="multisteps-form__input"
|
|
:class="{ 'is-invalid': fieldErrors.first_name }"
|
|
type="text"
|
|
placeholder="ex. Jean"
|
|
maxlength="191"
|
|
@input="form.first_name = $event.target.value"
|
|
/>
|
|
<div v-if="fieldErrors.first_name" class="invalid-feedback">
|
|
{{ fieldErrors.first_name }}
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-sm-6 mt-3 mt-sm-0">
|
|
<label class="form-label"
|
|
>Nom de famille <span class="text-danger">*</span></label
|
|
>
|
|
<soft-input
|
|
:value="form.last_name"
|
|
class="multisteps-form__input"
|
|
:class="{ 'is-invalid': fieldErrors.last_name }"
|
|
type="text"
|
|
placeholder="ex. Dupont"
|
|
maxlength="191"
|
|
@input="form.last_name = $event.target.value"
|
|
/>
|
|
<div v-if="fieldErrors.last_name" class="invalid-feedback">
|
|
{{ fieldErrors.last_name }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Email & Téléphone -->
|
|
<div class="row mt-3">
|
|
<div class="col-12 col-sm-6">
|
|
<label class="form-label">Email</label>
|
|
<soft-input
|
|
:value="form.email"
|
|
class="multisteps-form__input"
|
|
:class="{ 'is-invalid': fieldErrors.email }"
|
|
type="email"
|
|
placeholder="ex. jean.dupont@entreprise.com"
|
|
maxlength="191"
|
|
@input="form.email = $event.target.value"
|
|
/>
|
|
<div v-if="fieldErrors.email" class="invalid-feedback">
|
|
{{ fieldErrors.email }}
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-sm-6 mt-3 mt-sm-0">
|
|
<label class="form-label">Téléphone</label>
|
|
<soft-input
|
|
:value="form.phone"
|
|
class="multisteps-form__input"
|
|
:class="{ 'is-invalid': fieldErrors.phone }"
|
|
type="text"
|
|
placeholder="ex. +261341234567"
|
|
maxlength="50"
|
|
@input="form.phone = $event.target.value"
|
|
/>
|
|
<div v-if="fieldErrors.phone" class="invalid-feedback">
|
|
{{ fieldErrors.phone }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Intitulé du poste -->
|
|
<div class="row mt-3">
|
|
<div class="col-12">
|
|
<label class="form-label">Intitulé du poste</label>
|
|
<soft-input
|
|
:value="form.job_title"
|
|
class="multisteps-form__input"
|
|
:class="{ 'is-invalid': fieldErrors.job_title }"
|
|
type="text"
|
|
placeholder="ex. Développeur Full-Stack"
|
|
maxlength="191"
|
|
@input="form.job_title = $event.target.value"
|
|
/>
|
|
<div v-if="fieldErrors.job_title" class="invalid-feedback">
|
|
{{ fieldErrors.job_title }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Date d'embauche -->
|
|
<div class="row mt-3">
|
|
<div class="col-12 col-sm-6">
|
|
<label class="form-label">Date d'embauche</label>
|
|
<soft-input
|
|
:value="form.hire_date"
|
|
class="multisteps-form__input"
|
|
:class="{ 'is-invalid': fieldErrors.hire_date }"
|
|
type="date"
|
|
@input="form.hire_date = $event.target.value"
|
|
/>
|
|
<div v-if="fieldErrors.hire_date" class="invalid-feedback">
|
|
{{ fieldErrors.hire_date }}
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-sm-6 mt-3 mt-sm-0">
|
|
<label class="form-label">Salaire</label>
|
|
<soft-input
|
|
:value="form.salary"
|
|
class="multisteps-form__input"
|
|
:class="{ 'is-invalid': fieldErrors.salary }"
|
|
type="number"
|
|
placeholder="ex. 45000"
|
|
step="0.01"
|
|
min="0"
|
|
@input="form.salary = $event.target.value"
|
|
/>
|
|
<div v-if="fieldErrors.salary" class="invalid-feedback">
|
|
{{ fieldErrors.salary }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statut actif -->
|
|
<div class="row mt-3">
|
|
<div class="col-12">
|
|
<div class="form-check form-switch">
|
|
<input
|
|
id="isActive"
|
|
class="form-check-input"
|
|
type="checkbox"
|
|
:checked="form.active"
|
|
@change="form.active = $event.target.checked"
|
|
/>
|
|
<label class="form-check-label" for="isActive">
|
|
Employé actif
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Boutons -->
|
|
<div class="button-row d-flex mt-4">
|
|
<soft-button
|
|
type="button"
|
|
color="secondary"
|
|
variant="outline"
|
|
class="me-2 mb-0"
|
|
@click="resetForm"
|
|
>
|
|
Réinitialiser
|
|
</soft-button>
|
|
<soft-button
|
|
type="button"
|
|
color="dark"
|
|
variant="gradient"
|
|
class="ms-auto mb-0"
|
|
:disabled="props.loading"
|
|
@click="submitForm"
|
|
>
|
|
<span
|
|
v-if="props.loading"
|
|
class="spinner-border spinner-border-sm me-2"
|
|
role="status"
|
|
></span>
|
|
{{ props.loading ? "Création..." : "Créer l'employé" }}
|
|
</soft-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, defineProps, defineEmits, watch } from "vue";
|
|
import SoftInput from "@/components/SoftInput.vue";
|
|
import SoftButton from "@/components/SoftButton.vue";
|
|
|
|
// Props
|
|
const props = defineProps({
|
|
loading: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
validationErrors: {
|
|
type: Object,
|
|
default: () => ({}),
|
|
},
|
|
success: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
});
|
|
|
|
// Emits
|
|
const emit = defineEmits(["createEmployee"]);
|
|
|
|
// Reactive data
|
|
const errors = ref([]);
|
|
const fieldErrors = ref({});
|
|
|
|
const form = ref({
|
|
first_name: "",
|
|
last_name: "",
|
|
email: "",
|
|
phone: "",
|
|
job_title: "",
|
|
hire_date: "",
|
|
salary: null,
|
|
active: true,
|
|
});
|
|
|
|
// Watch for validation errors from parent
|
|
watch(
|
|
() => props.validationErrors,
|
|
(newErrors) => {
|
|
fieldErrors.value = { ...newErrors };
|
|
},
|
|
{ deep: true }
|
|
);
|
|
|
|
// Watch for success from parent
|
|
watch(
|
|
() => props.success,
|
|
(newSuccess) => {
|
|
if (newSuccess) {
|
|
resetForm();
|
|
}
|
|
}
|
|
);
|
|
|
|
const submitForm = async () => {
|
|
// Clear errors before submitting
|
|
fieldErrors.value = {};
|
|
errors.value = [];
|
|
|
|
// Clean up form data: convert empty strings to null
|
|
const cleanedForm = {};
|
|
const formData = form.value;
|
|
|
|
for (const [key, value] of Object.entries(formData)) {
|
|
if (value === "" || value === null || value === undefined) {
|
|
cleanedForm[key] = null;
|
|
} else {
|
|
cleanedForm[key] = value;
|
|
}
|
|
}
|
|
|
|
// Ensure active is boolean
|
|
cleanedForm.active = Boolean(formData.active);
|
|
|
|
// Convert salary to number if provided
|
|
if (cleanedForm.salary !== null) {
|
|
cleanedForm.salary = parseFloat(cleanedForm.salary);
|
|
}
|
|
|
|
console.log("Form data being emitted:", cleanedForm);
|
|
|
|
// Emit the cleaned form data to parent
|
|
emit("createEmployee", cleanedForm);
|
|
};
|
|
|
|
const resetForm = () => {
|
|
form.value = {
|
|
first_name: "",
|
|
last_name: "",
|
|
email: "",
|
|
phone: "",
|
|
job_title: "",
|
|
hire_date: "",
|
|
salary: null,
|
|
active: true,
|
|
};
|
|
clearErrors();
|
|
};
|
|
|
|
const clearErrors = () => {
|
|
errors.value = [];
|
|
fieldErrors.value = {};
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.form-label {
|
|
font-weight: 600;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.text-danger {
|
|
color: #f5365c;
|
|
}
|
|
|
|
.invalid-feedback {
|
|
display: block;
|
|
}
|
|
|
|
.spinner-border-sm {
|
|
width: 1rem;
|
|
height: 1rem;
|
|
}
|
|
|
|
.alert {
|
|
border-radius: 0.75rem;
|
|
}
|
|
</style>
|