Habilidades Puntuadas #2
@ -8,6 +8,7 @@
|
||||
|
||||
use App\Http\Requests\StoreMainRoleSkills;
|
||||
use App\Http\Requests\UpdateMainRoleSkills;
|
||||
use App\Models\department;
|
||||
use App\Models\mainRole;
|
||||
use App\Models\MainRoleSkills;
|
||||
use App\Models\Score;
|
||||
@ -42,57 +43,89 @@ public function index()
|
||||
->orWhereIn('scored_id', $scores)
|
||||
->with([
|
||||
'mainRole:id,name,department_id',
|
||||
'mainRole.department:id,name,description',
|
||||
'skill:id,name,department_id',
|
||||
'skill:id,name',
|
||||
'score:id,alias'
|
||||
])
|
||||
->paginate(config('app.pagination'));
|
||||
|
||||
return $this->vuew('index', [
|
||||
'mainRoleSkills' => $mainRoleSkills
|
||||
]);
|
||||
$departments = department::select(['id', 'name', 'description'])->orderBy('name', 'ASC')->get();
|
||||
$mainRoles = mainRole::with(['department:id,name'])->orderBy('name', 'ASC')->get();
|
||||
$skills = Skill::with(['department:id,name'])->orderBy('name', 'ASC')->get();
|
||||
$scores = Score::orderBy('alias', 'ASC')->get();
|
||||
|
||||
|
||||
return $this->vuew('index', [
|
||||
'mainRoleSkills' => $mainRoleSkills,
|
||||
'departments' => $departments,
|
||||
'mainRoles' => $mainRoles,
|
||||
'skills' => $skills,
|
||||
'scores' => $scores,
|
||||
]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$mainRoles = mainRole::with('department:id,name')->orderBy('name', 'ASC')->get();
|
||||
$skills = Skill::with('department:id,name')->orderBy('name', 'ASC')->get();
|
||||
$scores = Score::orderBy('alias', 'ASC')->get();
|
||||
$roleId = request()->get('role_id');
|
||||
$departmentId = request()->get('department_id');
|
||||
|
||||
return $this->vuew('create', [
|
||||
'mainRoles' => $mainRoles,
|
||||
'skills' => $skills,
|
||||
'scores' => $scores
|
||||
]);
|
||||
// Obtener el rol seleccionado y su departamento
|
||||
$selectedRole = null;
|
||||
$selectedDepartment = null;
|
||||
|
||||
if ($roleId) {
|
||||
$selectedRole = mainRole::with('department:id,name')->find($roleId);
|
||||
if ($selectedRole && $selectedRole->department) {
|
||||
$selectedDepartment = $selectedRole->department;
|
||||
}
|
||||
} elseif ($departmentId) {
|
||||
$selectedDepartment = department::find($departmentId);
|
||||
}
|
||||
$mainRoles = mainRole::with('department:id,name')->orderBy('name', 'ASC')->get();
|
||||
$skills = Skill::with('department:id,name')->orderBy('name', 'ASC')->get();
|
||||
$scores = Score::orderBy('alias', 'ASC')->get();
|
||||
|
||||
return $this->vuew('create', [
|
||||
'mainRoles' => $mainRoles,
|
||||
'skills' => $skills,
|
||||
'scores' => $scores,
|
||||
'selectedRole' => $selectedRole,
|
||||
'selectedDepartment' => $selectedDepartment
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(StoreMainRoleSkills $request)
|
||||
{
|
||||
$create = [];
|
||||
foreach ($request['skills'] as $skill){
|
||||
$create[] = [
|
||||
'main_role_id' => $request['main_role_id'],
|
||||
'skill_id' => $skill['skill_id'],
|
||||
'scored_id' => $skill['scored_id'],
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
}
|
||||
$create = [];
|
||||
foreach ($request['skills'] as $skill) {
|
||||
$create[] = [
|
||||
'main_role_id' => $request['main_role_id'],
|
||||
'skill_id' => $skill['skill_id'],
|
||||
'scored_id' => $skill['scored_id'],
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
}
|
||||
|
||||
MainRoleSkills::insert($create);
|
||||
MainRoleSkills::insert($create);
|
||||
|
||||
return $this->index();
|
||||
return $this->index();
|
||||
}
|
||||
|
||||
public function update(UpdateMainRoleSkills $request, MainRoleSkills $mainRoleSkills)
|
||||
{
|
||||
$mainRoleSkills->update($request->all());
|
||||
$mainRoleSkills->update($request->all());
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
public function destroy($mainRoleId)
|
||||
{
|
||||
$mainRoleSkill = MainRoleSkills::findOrFail($id);
|
||||
$mainRoleSkill->delete();
|
||||
}
|
||||
try {
|
||||
// Eliminar todas las habilidades asociadas a este rol principal
|
||||
MainRoleSkills::where('main_role_id', $mainRoleId)->delete();
|
||||
|
||||
return $this->index();
|
||||
} catch (\Throwable $th) {
|
||||
Log::channel('mainRoleSkills')->error($th->getMessage());
|
||||
return response()->json(['error' => 'Error al eliminar las habilidades'], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,13 +4,11 @@ defineEmits(['select']);
|
||||
const props = defineProps({
|
||||
departments: Object,
|
||||
});
|
||||
|
||||
console.log('Department props:', props.departments);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<div class="mb-6">
|
||||
<div class="mb-6 mt-12">
|
||||
<h2 class="text-xl font-semibold mb-2">{{ $t('department.select.title') }}</h2>
|
||||
<p class="text-gray-600">{{ $t('department.select.description') }}</p>
|
||||
</div>
|
||||
|
||||
@ -4,14 +4,8 @@ import { computed } from 'vue';
|
||||
const emit = defineEmits(['select']);
|
||||
|
||||
const props = defineProps({
|
||||
mainRoles: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
selectedDepartment: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
mainRoles: Object,
|
||||
selectedDepartment: Object,
|
||||
});
|
||||
|
||||
const departmentMainRoles = computed(() => {
|
||||
@ -23,9 +17,9 @@ const departmentMainRoles = computed(() => {
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<div class="mb-6">
|
||||
<div class="mb-6 mt-12">
|
||||
<h2 class="text-xl font-semibold mb-2">
|
||||
{{ $t('mainRole.inDepartment', { department: selectedDepartment.name }) }}
|
||||
{{ $t('mainRole.inDepartment') }}
|
||||
</h2>
|
||||
<p class="text-gray-600">{{ $t('mainRole.select.description') }}</p>
|
||||
</div>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, watch } from "vue";
|
||||
import { ref, computed } from "vue";
|
||||
import PrimaryButton from "@/Components/Dashboard/Button/Primary.vue";
|
||||
import Selectable from "@/Components/Dashboard/Form/Selectable.vue";
|
||||
|
||||
const emit = defineEmits(["submit", "update:modelValue"]);
|
||||
const emit = defineEmits(["submit"]);
|
||||
|
||||
const props = defineProps({
|
||||
form: {
|
||||
@ -11,11 +11,11 @@ const props = defineProps({
|
||||
required: true,
|
||||
},
|
||||
skills: {
|
||||
type: Object,
|
||||
type: Array, // Ya filtradas por departamento
|
||||
required: true,
|
||||
},
|
||||
scores: {
|
||||
type: Object,
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
selectedDepartment: {
|
||||
@ -26,56 +26,49 @@ const props = defineProps({
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const skillId = ref("");
|
||||
const scoredId = ref("");
|
||||
const todos = ref([]);
|
||||
|
||||
function addTodo() {
|
||||
if (skillId.value && scoredId.value) {
|
||||
// Buscar los objetos completos
|
||||
const selectedSkill = departmentSkills.value.find(
|
||||
(skill) => skill.id === skillId.value.id || skill.id === skillId.value
|
||||
const selectedSkill = props.skills.find(
|
||||
skill => skill.id === (skillId.value.id || skillId.value)
|
||||
);
|
||||
const selectedScore = props.scores.find(
|
||||
(score) => score.id === scoredId.value.id || score.id === scoredId.value
|
||||
score => score.id === (scoredId.value.id || scoredId.value)
|
||||
);
|
||||
|
||||
if (selectedSkill && selectedScore) {
|
||||
const newItem = {
|
||||
skill_id: selectedSkill.id,
|
||||
scored_id: selectedScore.id,
|
||||
skill_name: selectedSkill.name,
|
||||
score_alias: selectedScore.alias,
|
||||
};
|
||||
// Verificar que no esté duplicada
|
||||
const isDuplicate = todos.value.some(
|
||||
todo => todo.skill_id === selectedSkill.id
|
||||
);
|
||||
|
||||
todos.value.push(newItem);
|
||||
if (!isDuplicate) {
|
||||
const newItem = {
|
||||
skill_id: selectedSkill.id,
|
||||
scored_id: selectedScore.id,
|
||||
skill_name: selectedSkill.name,
|
||||
score_alias: selectedScore.alias,
|
||||
};
|
||||
|
||||
// Limpiar los campos
|
||||
skillId.value = "";
|
||||
scoredId.value = "";
|
||||
todos.value.push(newItem);
|
||||
|
||||
// Emitir el arreglo actualizado
|
||||
emit("update:modelValue", todos.value);
|
||||
// Limpiar campos
|
||||
skillId.value = "";
|
||||
scoredId.value = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeTodo(index) {
|
||||
todos.value.splice(index, 1);
|
||||
emit("update:modelValue", todos.value);
|
||||
}
|
||||
|
||||
const departmentSkills = computed(() => {
|
||||
return props.skills.filter(
|
||||
(skill) => skill.department_id === props.selectedDepartment.id
|
||||
);
|
||||
});
|
||||
|
||||
const isFormValid = computed(() => {
|
||||
return todos.value.length > 0;
|
||||
});
|
||||
@ -85,35 +78,16 @@ const submitForm = () => {
|
||||
emit("submit", todos.value);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (Array.isArray(props.modelValue) && props.modelValue.length > 0) {
|
||||
todos.value = [...props.modelValue];
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
if (Array.isArray(newValue)) {
|
||||
todos.value = [...newValue];
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<div class="mb-6">
|
||||
<h2 class="text-xl font-semibold mb-2">
|
||||
{{ $t("skill.assignTo", { role: selectedMainRole.name }) }}
|
||||
Asignar Habilidades al Rol: {{ selectedMainRole.name }}
|
||||
</h2>
|
||||
<p class="text-gray-600">
|
||||
{{
|
||||
$t("skill.assignment.description", {
|
||||
department: selectedDepartment.name,
|
||||
})
|
||||
}}
|
||||
Departamento: {{ selectedDepartment.name }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -123,7 +97,7 @@ watch(
|
||||
<Selectable
|
||||
id="skill_id"
|
||||
v-model="skillId"
|
||||
:options="departmentSkills"
|
||||
:options="skills"
|
||||
:title="$t('skill.title')"
|
||||
placeholder="Selecciona una habilidad..."
|
||||
label="name"
|
||||
@ -147,7 +121,8 @@ watch(
|
||||
<button
|
||||
@click="addTodo"
|
||||
type="button"
|
||||
class="rounded-lg px-4 py-2 bg-primary text-white border dark:bg-primary-dark dark:border-primary-dark-on"
|
||||
:disabled="!skillId || !scoredId"
|
||||
class="rounded-lg px-4 py-2 bg-primary text-white border disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Agregar Habilidad
|
||||
</button>
|
||||
@ -159,15 +134,15 @@ watch(
|
||||
<template v-for="(todo, index) in todos" :key="index">
|
||||
<div class="flex bg-white items-center justify-between rounded-lg px-4 py-3 border shadow-sm">
|
||||
<div class="flex-1">
|
||||
<div class="font-bold text-black">Habilidad: {{ todo.skill_name }}</div>
|
||||
<div class="font-bold text-black">Puntuación: {{ todo.score_alias }}</div>
|
||||
<div class="font-bold text-black">{{ todo.skill_name }}</div>
|
||||
<div class="text-sm text-gray-600">Puntuación: {{ todo.score_alias }}</div>
|
||||
</div>
|
||||
<div
|
||||
<button
|
||||
@click="removeTodo(index)"
|
||||
class="rounded-lg px-4 py-2 bg-red-600 text-white cursor-pointer hover:bg-red-700 transition-colors"
|
||||
class="rounded-lg px-4 py-2 bg-red-600 text-white hover:bg-red-700 transition-colors"
|
||||
>
|
||||
Quitar
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -184,7 +159,7 @@ watch(
|
||||
type="button"
|
||||
@click="submitForm"
|
||||
>
|
||||
<span>{{ $t("Crear") }}</span>
|
||||
<span>Crear Asignación</span>
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
90
resources/js/Components/App/SkillsRole.vue
Normal file
90
resources/js/Components/App/SkillsRole.vue
Normal file
@ -0,0 +1,90 @@
|
||||
<script setup>
|
||||
import { computed } from "vue";
|
||||
import { Link } from "@inertiajs/vue3";
|
||||
import { can, goTo } from "@/Pages/Admin/MainRoleSkills/Component";
|
||||
|
||||
import GoogleIcon from "@/Components/Shared/GoogleIcon.vue";
|
||||
|
||||
const emit = defineEmits(["select"]);
|
||||
|
||||
const props = defineProps({
|
||||
mainRoleSkills: Array,
|
||||
selectedRole: Object,
|
||||
});
|
||||
|
||||
const roleSkills = computed(() => {
|
||||
if (!props.mainRoleSkills || !props.selectedRole) {
|
||||
return [];
|
||||
}
|
||||
// Filtrar habilidades por rol seleccionado
|
||||
return props.mainRoleSkills.filter(
|
||||
(roleSkill) => roleSkill.main_role_id === props.selectedRole.id
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<div class="mb-6 mt-12">
|
||||
<div class="flex gap-6">
|
||||
<h2 class="text-xl font-semibold mb-2">
|
||||
{{ $t("skill.assignment.title") }}
|
||||
</h2>
|
||||
<Link
|
||||
v-if="can('create')"
|
||||
:href="route(goTo('create'), {
|
||||
role_id: selectedRole.id,
|
||||
department_id: selectedRole.department?.id || selectedRole.department_id
|
||||
})"
|
||||
>
|
||||
<GoogleIcon
|
||||
:title="$t('crud.create')"
|
||||
class="btn-icon-primary"
|
||||
name="add"
|
||||
outline
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<p class="text-gray-600">
|
||||
Habilidades asignadas al rol: {{ selectedRole.name }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="roleSkills.length > 0"
|
||||
class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4"
|
||||
>
|
||||
<div
|
||||
v-for="roleSkill in roleSkills"
|
||||
:key="roleSkill.id"
|
||||
class="bg-white p-4 rounded-lg shadow hover:shadow-lg transition-shadow duration-200 cursor-pointer border"
|
||||
@click="emit('select', roleSkill)"
|
||||
>
|
||||
<h3 class="text-lg font-medium mb-2 text-blue-600">
|
||||
{{ roleSkill.skill.name }}
|
||||
</h3>
|
||||
|
||||
<div class="text-sm text-gray-600">
|
||||
<span class="font-semibold">Puntuación:</span>
|
||||
{{ roleSkill.score.alias }}
|
||||
</div>
|
||||
|
||||
<p
|
||||
v-if="roleSkill.skill.description"
|
||||
class="text-xs text-gray-500 mt-2"
|
||||
>
|
||||
{{ roleSkill.skill.description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="text-center py-12">
|
||||
<p class="text-gray-500 text-lg">
|
||||
No hay habilidades asignadas a este rol
|
||||
</p>
|
||||
<p class="text-gray-400 text-sm mt-2">
|
||||
Las habilidades se pueden asignar desde la sección de creación
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -292,6 +292,7 @@ export default {
|
||||
assignTo: "Asignar a rol principal",
|
||||
system: "Sistema de habilidades",
|
||||
assignment: {
|
||||
title: "Habilidades del Rol",
|
||||
description: "Asigna una habilidad a un rol principal.",
|
||||
},
|
||||
create: {
|
||||
|
||||
@ -65,7 +65,7 @@ const submit = () => form.post(route(goTo('store')), {
|
||||
required
|
||||
/>
|
||||
<Input
|
||||
id="Descrición"
|
||||
id="Descripción"
|
||||
class="col-span-2"
|
||||
v-model="form.description"
|
||||
:onError="form.errors.description"
|
||||
|
||||
@ -54,7 +54,7 @@ const submit = () => form.post(route(goTo('store')), {
|
||||
required
|
||||
/>
|
||||
<Input
|
||||
id="Descrición"
|
||||
id="Descripción"
|
||||
class="col-span-2"
|
||||
v-model="form.description"
|
||||
:onError="form.errors.description"
|
||||
|
||||
@ -59,7 +59,7 @@ const submit = () =>
|
||||
required
|
||||
/>
|
||||
<Input
|
||||
id="Descrición"
|
||||
id="Descripción"
|
||||
class="col-span-2"
|
||||
v-model="form.description"
|
||||
:onError="form.errors.description"
|
||||
|
||||
@ -1,163 +1,169 @@
|
||||
<script setup>
|
||||
import { goTo, transl } from "./Component";
|
||||
import { Link, useForm } from "@inertiajs/vue3";
|
||||
import { ref, computed } from "vue";
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
|
||||
import PrimaryButton from "@/Components/Dashboard/Button/Primary.vue";
|
||||
import SecondaryButton from "@/Components/Dashboard/Button/Secondary.vue";
|
||||
import PageHeader from "@/Components/Dashboard/PageHeader.vue";
|
||||
import GoogleIcon from "@/Components/Shared/GoogleIcon.vue";
|
||||
import DashboardLayout from "@/Layouts/DashboardLayout.vue";
|
||||
|
||||
// Componentes específicos para el flujo
|
||||
import StepIndicator from "@/Components/App/StepIndicator.vue";
|
||||
import DepartmentSelector from "@/Components/App/DepartmentSelector.vue";
|
||||
import MainRoleSelector from "@/Components/App/MainRoleSelector.vue";
|
||||
import SkillAssignment from "@/Components/App/SkillAssignment.vue";
|
||||
|
||||
const props = defineProps({
|
||||
mainRoles: Object,
|
||||
skills: Object,
|
||||
scores: Object,
|
||||
mainRoles: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
skills: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
scores: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
selectedRole: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
selectedDepartment: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
|
||||
// Estado del flujo
|
||||
const currentStep = ref(1);
|
||||
const selectedDepartment = ref(null);
|
||||
const selectedMainRole = ref(null);
|
||||
const selectedDepartment = ref(props.selectedDepartment);
|
||||
const selectedMainRole = ref(props.selectedRole);
|
||||
|
||||
const form = useForm({
|
||||
main_role_id: "",
|
||||
skill_id: "",
|
||||
scored_id: "",
|
||||
main_role_id: props.selectedRole?.id || '',
|
||||
skills: []
|
||||
});
|
||||
|
||||
// Datos computados
|
||||
const uniqueDepartments = computed(() => {
|
||||
const deps = [];
|
||||
const seen = new Set();
|
||||
// Obtener departamentos únicos
|
||||
const departments = computed(() => {
|
||||
const uniqueDepartments = [];
|
||||
const seenIds = new Set();
|
||||
|
||||
props.mainRoles.forEach((role) => {
|
||||
if (role.department && !seen.has(role.department.id)) {
|
||||
seen.add(role.department.id);
|
||||
deps.push(role.department);
|
||||
props.mainRoles.forEach(role => {
|
||||
if (role.department && !seenIds.has(role.department.id)) {
|
||||
seenIds.add(role.department.id);
|
||||
uniqueDepartments.push(role.department);
|
||||
}
|
||||
});
|
||||
|
||||
return deps;
|
||||
return uniqueDepartments;
|
||||
});
|
||||
|
||||
// Filtrar habilidades por departamento seleccionado
|
||||
const departmentSkills = computed(() => {
|
||||
if (!selectedDepartment.value) return [];
|
||||
return props.skills.filter(skill =>
|
||||
skill.department_id === selectedDepartment.value.id
|
||||
);
|
||||
});
|
||||
|
||||
// Métodos de navegación
|
||||
const handleDepartmentSelect = (department) => {
|
||||
selectedDepartment.value = department;
|
||||
currentStep.value = 2;
|
||||
selectedMainRole.value = null;
|
||||
form.main_role_id = '';
|
||||
};
|
||||
|
||||
const handleMainRoleSelect = (mainRole) => {
|
||||
selectedMainRole.value = mainRole;
|
||||
form.main_role_id = mainRole.id;
|
||||
currentStep.value = 3;
|
||||
const handleRoleSelect = (role) => {
|
||||
selectedMainRole.value = role;
|
||||
form.main_role_id = role.id;
|
||||
};
|
||||
|
||||
const submit = (skillsData) => {
|
||||
form.skills = skillsData;
|
||||
|
||||
form.post(route(goTo("store")), {
|
||||
onSuccess: () => {
|
||||
// Redireccionar al index
|
||||
},
|
||||
onError: () => {
|
||||
// Mostrar mensaje de error
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const goBack = () => {
|
||||
if (currentStep.value === 3) {
|
||||
if (selectedMainRole.value) {
|
||||
selectedMainRole.value = null;
|
||||
form.main_role_id = "";
|
||||
form.skill_id = "";
|
||||
form.scored_id = "";
|
||||
currentStep.value = 2;
|
||||
} else if (currentStep.value === 2) {
|
||||
form.main_role_id = '';
|
||||
} else if (selectedDepartment.value) {
|
||||
selectedDepartment.value = null;
|
||||
selectedMainRole.value = null;
|
||||
form.reset();
|
||||
currentStep.value = 1;
|
||||
}
|
||||
};
|
||||
|
||||
const resetFlow = () => {
|
||||
selectedDepartment.value = null;
|
||||
selectedMainRole.value = null;
|
||||
form.reset();
|
||||
currentStep.value = 1;
|
||||
};
|
||||
|
||||
const submit = (skillArray) => {
|
||||
const skillsData = skillArray.map(skill => ({
|
||||
skill_id: skill.skill_id,
|
||||
scored_id: skill.scored_id,
|
||||
}));
|
||||
form
|
||||
.transform((data) => ({
|
||||
main_role_id: data.main_role_id,
|
||||
skills: skillsData
|
||||
}))
|
||||
.post(route(goTo("store")), {
|
||||
onSuccess: () => {
|
||||
Notify.success(transl("create.onSuccess"));
|
||||
resetFlow();
|
||||
},
|
||||
onError: () => Notify.error(transl("create.onError")),
|
||||
});
|
||||
};
|
||||
onMounted(() => {
|
||||
// Si tenemos rol seleccionado, asegurar que el departamento también esté seleccionado
|
||||
if (props.selectedRole && props.selectedRole.department) {
|
||||
selectedDepartment.value = props.selectedRole.department;
|
||||
selectedMainRole.value = props.selectedRole;
|
||||
form.main_role_id = props.selectedRole.id;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DashboardLayout :title="transl('create.title')">
|
||||
<PageHeader>
|
||||
<div class="flex items-center space-x-2">
|
||||
<Link :href="route(goTo('index'))">
|
||||
<GoogleIcon
|
||||
:title="$t('return')"
|
||||
class="btn-icon-primary"
|
||||
name="arrow_back"
|
||||
outline
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<Link :href="route(goTo('index'))">
|
||||
<GoogleIcon
|
||||
:title="$t('return')"
|
||||
class="btn-icon-primary"
|
||||
name="arrow_back"
|
||||
outline
|
||||
/>
|
||||
</Link>
|
||||
</PageHeader>
|
||||
|
||||
<!-- Indicador de progreso componentizado -->
|
||||
<div class="mt-6">
|
||||
<StepIndicator :current-step="currentStep" />
|
||||
<div class="flex justify-center gap-2">
|
||||
<SecondaryButton v-if="currentStep > 1" @click="goBack" type="button">
|
||||
<GoogleIcon name="keyboard_backspace" class="mr-2" />
|
||||
{{ $t("Regresar") }}
|
||||
</SecondaryButton>
|
||||
|
||||
<SecondaryButton
|
||||
v-if="currentStep > 1"
|
||||
@click="resetFlow"
|
||||
type="button"
|
||||
>
|
||||
<GoogleIcon name="refresh" class="mr-2" />
|
||||
{{ $t("Inicio") }}
|
||||
</SecondaryButton>
|
||||
<div class="w-full pb-8">
|
||||
<div class="mt-8">
|
||||
<p v-text="transl('create.description')" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Componentes de pasos -->
|
||||
<DepartmentSelector
|
||||
v-if="currentStep === 1"
|
||||
:departments="uniqueDepartments"
|
||||
@select="handleDepartmentSelect"
|
||||
/>
|
||||
<div class="w-full">
|
||||
<!-- Navegación hacia atrás -->
|
||||
<div v-if="selectedDepartment || selectedMainRole" class="mb-6">
|
||||
<button
|
||||
@click="goBack"
|
||||
class="text-primary hover:underline flex items-center gap-2"
|
||||
>
|
||||
<GoogleIcon name="arrow_back" class="w-4 h-4" />
|
||||
<span v-if="selectedMainRole">Cambiar rol principal</span>
|
||||
<span v-else-if="selectedDepartment">Cambiar departamento</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<MainRoleSelector
|
||||
v-if="currentStep === 2"
|
||||
:main-roles="mainRoles"
|
||||
:selected-department="selectedDepartment"
|
||||
@select="handleMainRoleSelect"
|
||||
/>
|
||||
<!-- Paso 1: Selector de Departamento -->
|
||||
<DepartmentSelector
|
||||
v-if="!selectedDepartment"
|
||||
:departments="departments"
|
||||
@select="handleDepartmentSelect"
|
||||
/>
|
||||
|
||||
<SkillAssignment
|
||||
v-if="currentStep === 3"
|
||||
:form="form"
|
||||
:skills="skills"
|
||||
:scores="scores"
|
||||
:selected-department="selectedDepartment"
|
||||
:selected-main-role="selectedMainRole"
|
||||
@submit="submit"
|
||||
/>
|
||||
<!-- Paso 2: Selector de Rol Principal -->
|
||||
<MainRoleSelector
|
||||
v-else-if="selectedDepartment && !selectedMainRole"
|
||||
:selectedDepartment="selectedDepartment"
|
||||
:mainRoles="mainRoles"
|
||||
@select="handleRoleSelect"
|
||||
/>
|
||||
|
||||
<!-- Paso 3: Asignación de Habilidades -->
|
||||
<SkillAssignment
|
||||
v-else-if="selectedDepartment && selectedMainRole"
|
||||
:form="form"
|
||||
:skills="departmentSkills"
|
||||
:scores="scores"
|
||||
:selectedDepartment="selectedDepartment"
|
||||
:selectedMainRole="selectedMainRole"
|
||||
@submit="submit"
|
||||
/>
|
||||
</div>
|
||||
</DashboardLayout>
|
||||
</template>
|
||||
|
||||
@ -1,146 +1,91 @@
|
||||
<script setup>
|
||||
import { transl, can, goTo } from "./Component";
|
||||
import { transl } from "./Component";
|
||||
import { ref, computed } from "vue";
|
||||
import { Link } from "@inertiajs/vue3";
|
||||
|
||||
import ModalController from "@/Controllers/ModalController.js";
|
||||
import SearcherController from "@/Controllers/SearcherController.js";
|
||||
|
||||
import SearcherHead from "@/Components/Dashboard/Searcher.vue";
|
||||
import Table from "@/Components/Dashboard/Table.vue";
|
||||
import GoogleIcon from "@/Components/Shared/GoogleIcon.vue";
|
||||
import DashboardLayout from "@/Layouts/DashboardLayout.vue";
|
||||
import DestroyView from "./Destroy.vue";
|
||||
import EditView from "./Edit.vue";
|
||||
import DepartmentSelector from "@/Components/App/DepartmentSelector.vue";
|
||||
import MainRoleSelector from "@/Components/App/MainRoleSelector.vue";
|
||||
import SkillRole from "@/Components/App/SkillsRole.vue";
|
||||
import PageHeader from "@/Components/Dashboard/PageHeader.vue";
|
||||
import GoogleIcon from "@/Components/Shared/GoogleIcon.vue";
|
||||
|
||||
defineEmits(["select"]);
|
||||
|
||||
const props = defineProps({
|
||||
mainRoleSkills: Object,
|
||||
departments: Object,
|
||||
mainRoles: Object,
|
||||
skills: Object,
|
||||
scores: Object,
|
||||
});
|
||||
|
||||
// Controladores
|
||||
const Modal = new ModalController();
|
||||
const Searcher = new SearcherController(goTo("index"));
|
||||
const selectedDepartment = ref(null);
|
||||
const selectedRole = ref(null);
|
||||
|
||||
// Variables de controladores
|
||||
const destroyModal = ref(Modal.destroyModal);
|
||||
const editModal = ref(Modal.editModal);
|
||||
const modelModal = ref(Modal.modelModal);
|
||||
const query = ref(Searcher.query);
|
||||
const filteredMainRoleSkills = computed(() => {
|
||||
if (!props.mainRoleSkills?.data || !selectedRole.value) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const groupedRoles = computed(() => {
|
||||
const groups = {};
|
||||
props.mainRoleSkills.data?.forEach(item => {
|
||||
const key = item.main_role?.id;
|
||||
if(!groups[key]) groups[key] = { ...item, skills: []};
|
||||
groups[key].skills.push(item);
|
||||
})
|
||||
return Object.values(groups);
|
||||
return props.mainRoleSkills.data.filter(roleSkill =>
|
||||
roleSkill.main_role_id === selectedRole.value.id
|
||||
);
|
||||
});
|
||||
|
||||
const handleDepartmentSelect = (department) => {
|
||||
selectedDepartment.value = department;
|
||||
selectedRole.value= null;
|
||||
};
|
||||
const handleRoleSelect = (role) => {
|
||||
selectedRole.value = role;
|
||||
};
|
||||
|
||||
const handleSkillSelect = (roleSkill) => {
|
||||
console.log('Habilidad de rol seleccionada:', roleSkill);
|
||||
}
|
||||
|
||||
const goBack = () => {
|
||||
if (selectedRole.value) {
|
||||
selectedRole.value = null;
|
||||
} else if (selectedDepartment.value) {
|
||||
selectedDepartment.value = null;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DashboardLayout :title="transl('system')">
|
||||
<SearcherHead @search="Searcher.search">
|
||||
<Link v-if="can('create')" :href="route(goTo('create'))">
|
||||
<PageHeader>
|
||||
<button @click="goBack">
|
||||
<GoogleIcon
|
||||
:title="$t('crud.create')"
|
||||
:title="$t('return')"
|
||||
class="btn-icon-primary"
|
||||
name="add"
|
||||
name="arrow_back"
|
||||
outline
|
||||
/>
|
||||
</Link>
|
||||
</SearcherHead>
|
||||
<div class="pt-2 w-full">
|
||||
<Table
|
||||
:items="mainRoleSkills"
|
||||
@send-pagination="Searcher.searchWithPagination"
|
||||
>
|
||||
<template #head>
|
||||
<th class="table-item" v-text="$t('Departamento')" />
|
||||
<th class="table-item" v-text="$t('Rol Principal')" />
|
||||
<th class="table-item" v-text="$t('Habilidades Puntuadas')" />
|
||||
<th class="table-item w-44" v-text="$t('actions')" />
|
||||
</template>
|
||||
<template #body="{ items }">
|
||||
<tr v-for="group in groupedRoles" :key="group.main_role.id">
|
||||
<td class="table-item border">
|
||||
<div class="flex items-center text-sm">
|
||||
<div>
|
||||
<p class="font-semibold">
|
||||
{{ group.main_role?.department?.name }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="table-item border">
|
||||
<div class="flex items-center text-sm">
|
||||
<div>
|
||||
<p class="font-semibold">
|
||||
{{ group.main_role?.name }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<div>
|
||||
<td class="table-item border">
|
||||
<div class="flex items-center text-sm">
|
||||
<div>
|
||||
<p v-for="skill in group.skills" :key="skill.id" class="font-semibold">
|
||||
{{ skill.skill?.name }} - {{ skill.score?.alias }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</div>
|
||||
<td class="table-item border">
|
||||
<div class="flex justify-center space-x-2">
|
||||
<GoogleIcon
|
||||
v-if="can('edit')"
|
||||
:title="$t('crud.edit')"
|
||||
class="btn-icon-primary"
|
||||
name="edit"
|
||||
outline
|
||||
@click="Modal.switchEditModal(model)"
|
||||
/>
|
||||
<GoogleIcon
|
||||
v-if="can('destroy')"
|
||||
:title="$t('crud.destroy')"
|
||||
class="btn-icon-primary"
|
||||
name="delete"
|
||||
outline
|
||||
@click="Modal.switchDestroyModal(model)"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<template #empty>
|
||||
<td class="table-item border">
|
||||
<div class="flex items-center text-sm">
|
||||
<div>
|
||||
<p class="font-semibold">
|
||||
{{ $t("registers.empty") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="table-item border">-</td>
|
||||
<td class="table-item border">-</td>
|
||||
</template>
|
||||
</Table>
|
||||
</div>
|
||||
<EditView
|
||||
v-if="can('edit')"
|
||||
:show="editModal"
|
||||
:model="modelModal"
|
||||
@switchModal="Modal.switchShowEditModal"
|
||||
@close="Modal.switchEditModal"
|
||||
</button>
|
||||
</PageHeader>
|
||||
|
||||
<!-- Selector de Departamento -->
|
||||
<DepartmentSelector
|
||||
v-if="!selectedDepartment"
|
||||
:departments="departments"
|
||||
@select="handleDepartmentSelect"
|
||||
/>
|
||||
<DestroyView
|
||||
v-if="can('create')"
|
||||
:show="destroyModal"
|
||||
:model="modelModal"
|
||||
@close="Modal.switchDestroyModal"
|
||||
|
||||
<!-- Selector de Roles Principales -->
|
||||
<MainRoleSelector
|
||||
v-if="selectedDepartment"
|
||||
:selectedDepartment="selectedDepartment"
|
||||
:mainRoles="mainRoles"
|
||||
@select="handleRoleSelect"
|
||||
/>
|
||||
|
||||
<SkillRole
|
||||
v-if="selectedRole"
|
||||
:mainRoleSkills="filteredMainRoleSkills"
|
||||
:selectedRole="selectedRole"
|
||||
@select="handleSkillSelect"
|
||||
/>
|
||||
</DashboardLayout>
|
||||
</template>
|
||||
|
||||
@ -59,7 +59,7 @@ const submit = () =>
|
||||
required
|
||||
/>
|
||||
<Input
|
||||
id="Descrición"
|
||||
id="Descripción"
|
||||
class="col-span-2"
|
||||
v-model="form.description"
|
||||
:onError="form.errors.description"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user