Terminar Habilidades puntuadas
This commit is contained in:
parent
d54c017d40
commit
776ef80b93
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
use App\Http\Requests\StoreMainRoleSkills;
|
use App\Http\Requests\StoreMainRoleSkills;
|
||||||
use App\Http\Requests\UpdateMainRoleSkills;
|
use App\Http\Requests\UpdateMainRoleSkills;
|
||||||
|
use App\Models\department;
|
||||||
use App\Models\mainRole;
|
use App\Models\mainRole;
|
||||||
use App\Models\MainRoleSkills;
|
use App\Models\MainRoleSkills;
|
||||||
use App\Models\Score;
|
use App\Models\Score;
|
||||||
@ -42,57 +43,89 @@ public function index()
|
|||||||
->orWhereIn('scored_id', $scores)
|
->orWhereIn('scored_id', $scores)
|
||||||
->with([
|
->with([
|
||||||
'mainRole:id,name,department_id',
|
'mainRole:id,name,department_id',
|
||||||
'mainRole.department:id,name,description',
|
'skill:id,name',
|
||||||
'skill:id,name,department_id',
|
|
||||||
'score:id,alias'
|
'score:id,alias'
|
||||||
])
|
])
|
||||||
->paginate(config('app.pagination'));
|
->paginate(config('app.pagination'));
|
||||||
|
|
||||||
return $this->vuew('index', [
|
$departments = department::select(['id', 'name', 'description'])->orderBy('name', 'ASC')->get();
|
||||||
'mainRoleSkills' => $mainRoleSkills
|
$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()
|
public function create()
|
||||||
{
|
{
|
||||||
$mainRoles = mainRole::with('department:id,name')->orderBy('name', 'ASC')->get();
|
$roleId = request()->get('role_id');
|
||||||
$skills = Skill::with('department:id,name')->orderBy('name', 'ASC')->get();
|
$departmentId = request()->get('department_id');
|
||||||
$scores = Score::orderBy('alias', 'ASC')->get();
|
|
||||||
|
|
||||||
return $this->vuew('create', [
|
// Obtener el rol seleccionado y su departamento
|
||||||
'mainRoles' => $mainRoles,
|
$selectedRole = null;
|
||||||
'skills' => $skills,
|
$selectedDepartment = null;
|
||||||
'scores' => $scores
|
|
||||||
]);
|
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)
|
public function store(StoreMainRoleSkills $request)
|
||||||
{
|
{
|
||||||
$create = [];
|
$create = [];
|
||||||
foreach ($request['skills'] as $skill){
|
foreach ($request['skills'] as $skill) {
|
||||||
$create[] = [
|
$create[] = [
|
||||||
'main_role_id' => $request['main_role_id'],
|
'main_role_id' => $request['main_role_id'],
|
||||||
'skill_id' => $skill['skill_id'],
|
'skill_id' => $skill['skill_id'],
|
||||||
'scored_id' => $skill['scored_id'],
|
'scored_id' => $skill['scored_id'],
|
||||||
'created_at' => now(),
|
'created_at' => now(),
|
||||||
'updated_at' => now(),
|
'updated_at' => now(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
MainRoleSkills::insert($create);
|
MainRoleSkills::insert($create);
|
||||||
|
|
||||||
return $this->index();
|
return $this->index();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(UpdateMainRoleSkills $request, MainRoleSkills $mainRoleSkills)
|
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);
|
try {
|
||||||
$mainRoleSkill->delete();
|
// 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({
|
const props = defineProps({
|
||||||
departments: Object,
|
departments: Object,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Department props:', props.departments);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full">
|
<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>
|
<h2 class="text-xl font-semibold mb-2">{{ $t('department.select.title') }}</h2>
|
||||||
<p class="text-gray-600">{{ $t('department.select.description') }}</p>
|
<p class="text-gray-600">{{ $t('department.select.description') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,14 +4,8 @@ import { computed } from 'vue';
|
|||||||
const emit = defineEmits(['select']);
|
const emit = defineEmits(['select']);
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
mainRoles: {
|
mainRoles: Object,
|
||||||
type: Object,
|
selectedDepartment: Object,
|
||||||
required: true
|
|
||||||
},
|
|
||||||
selectedDepartment: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const departmentMainRoles = computed(() => {
|
const departmentMainRoles = computed(() => {
|
||||||
@ -23,9 +17,9 @@ const departmentMainRoles = computed(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div class="mb-6">
|
<div class="mb-6 mt-12">
|
||||||
<h2 class="text-xl font-semibold mb-2">
|
<h2 class="text-xl font-semibold mb-2">
|
||||||
{{ $t('mainRole.inDepartment', { department: selectedDepartment.name }) }}
|
{{ $t('mainRole.inDepartment') }}
|
||||||
</h2>
|
</h2>
|
||||||
<p class="text-gray-600">{{ $t('mainRole.select.description') }}</p>
|
<p class="text-gray-600">{{ $t('mainRole.select.description') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, watch } from "vue";
|
import { ref, computed } from "vue";
|
||||||
import PrimaryButton from "@/Components/Dashboard/Button/Primary.vue";
|
import PrimaryButton from "@/Components/Dashboard/Button/Primary.vue";
|
||||||
import Selectable from "@/Components/Dashboard/Form/Selectable.vue";
|
import Selectable from "@/Components/Dashboard/Form/Selectable.vue";
|
||||||
|
|
||||||
const emit = defineEmits(["submit", "update:modelValue"]);
|
const emit = defineEmits(["submit"]);
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
form: {
|
form: {
|
||||||
@ -11,11 +11,11 @@ const props = defineProps({
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
skills: {
|
skills: {
|
||||||
type: Object,
|
type: Array, // Ya filtradas por departamento
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
scores: {
|
scores: {
|
||||||
type: Object,
|
type: Array,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
selectedDepartment: {
|
selectedDepartment: {
|
||||||
@ -26,56 +26,49 @@ const props = defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
modelValue: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const skillId = ref("");
|
const skillId = ref("");
|
||||||
const scoredId = ref("");
|
const scoredId = ref("");
|
||||||
const todos = ref([]);
|
const todos = ref([]);
|
||||||
|
|
||||||
function addTodo() {
|
function addTodo() {
|
||||||
if (skillId.value && scoredId.value) {
|
if (skillId.value && scoredId.value) {
|
||||||
// Buscar los objetos completos
|
const selectedSkill = props.skills.find(
|
||||||
const selectedSkill = departmentSkills.value.find(
|
skill => skill.id === (skillId.value.id || skillId.value)
|
||||||
(skill) => skill.id === skillId.value.id || skill.id === skillId.value
|
|
||||||
);
|
);
|
||||||
const selectedScore = props.scores.find(
|
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) {
|
if (selectedSkill && selectedScore) {
|
||||||
const newItem = {
|
// Verificar que no esté duplicada
|
||||||
skill_id: selectedSkill.id,
|
const isDuplicate = todos.value.some(
|
||||||
scored_id: selectedScore.id,
|
todo => todo.skill_id === selectedSkill.id
|
||||||
skill_name: selectedSkill.name,
|
);
|
||||||
score_alias: selectedScore.alias,
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
todos.value.push(newItem);
|
||||||
skillId.value = "";
|
|
||||||
scoredId.value = "";
|
|
||||||
|
|
||||||
// Emitir el arreglo actualizado
|
// Limpiar campos
|
||||||
emit("update:modelValue", todos.value);
|
skillId.value = "";
|
||||||
|
scoredId.value = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeTodo(index) {
|
function removeTodo(index) {
|
||||||
todos.value.splice(index, 1);
|
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(() => {
|
const isFormValid = computed(() => {
|
||||||
return todos.value.length > 0;
|
return todos.value.length > 0;
|
||||||
});
|
});
|
||||||
@ -85,35 +78,16 @@ const submitForm = () => {
|
|||||||
emit("submit", todos.value);
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<h2 class="text-xl font-semibold mb-2">
|
<h2 class="text-xl font-semibold mb-2">
|
||||||
{{ $t("skill.assignTo", { role: selectedMainRole.name }) }}
|
Asignar Habilidades al Rol: {{ selectedMainRole.name }}
|
||||||
</h2>
|
</h2>
|
||||||
<p class="text-gray-600">
|
<p class="text-gray-600">
|
||||||
{{
|
Departamento: {{ selectedDepartment.name }}
|
||||||
$t("skill.assignment.description", {
|
|
||||||
department: selectedDepartment.name,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -123,7 +97,7 @@ watch(
|
|||||||
<Selectable
|
<Selectable
|
||||||
id="skill_id"
|
id="skill_id"
|
||||||
v-model="skillId"
|
v-model="skillId"
|
||||||
:options="departmentSkills"
|
:options="skills"
|
||||||
:title="$t('skill.title')"
|
:title="$t('skill.title')"
|
||||||
placeholder="Selecciona una habilidad..."
|
placeholder="Selecciona una habilidad..."
|
||||||
label="name"
|
label="name"
|
||||||
@ -147,7 +121,8 @@ watch(
|
|||||||
<button
|
<button
|
||||||
@click="addTodo"
|
@click="addTodo"
|
||||||
type="button"
|
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
|
Agregar Habilidad
|
||||||
</button>
|
</button>
|
||||||
@ -159,15 +134,15 @@ watch(
|
|||||||
<template v-for="(todo, index) in todos" :key="index">
|
<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 bg-white items-center justify-between rounded-lg px-4 py-3 border shadow-sm">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="font-bold text-black">Habilidad: {{ todo.skill_name }}</div>
|
<div class="font-bold text-black">{{ todo.skill_name }}</div>
|
||||||
<div class="font-bold text-black">Puntuación: {{ todo.score_alias }}</div>
|
<div class="text-sm text-gray-600">Puntuación: {{ todo.score_alias }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<button
|
||||||
@click="removeTodo(index)"
|
@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
|
Quitar
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -184,7 +159,7 @@ watch(
|
|||||||
type="button"
|
type="button"
|
||||||
@click="submitForm"
|
@click="submitForm"
|
||||||
>
|
>
|
||||||
<span>{{ $t("Crear") }}</span>
|
<span>Crear Asignación</span>
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
</div>
|
</div>
|
||||||
</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",
|
assignTo: "Asignar a rol principal",
|
||||||
system: "Sistema de habilidades",
|
system: "Sistema de habilidades",
|
||||||
assignment: {
|
assignment: {
|
||||||
|
title: "Habilidades del Rol",
|
||||||
description: "Asigna una habilidad a un rol principal.",
|
description: "Asigna una habilidad a un rol principal.",
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
|
|||||||
@ -65,7 +65,7 @@ const submit = () => form.post(route(goTo('store')), {
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
id="Descrición"
|
id="Descripción"
|
||||||
class="col-span-2"
|
class="col-span-2"
|
||||||
v-model="form.description"
|
v-model="form.description"
|
||||||
:onError="form.errors.description"
|
:onError="form.errors.description"
|
||||||
|
|||||||
@ -54,7 +54,7 @@ const submit = () => form.post(route(goTo('store')), {
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
id="Descrición"
|
id="Descripción"
|
||||||
class="col-span-2"
|
class="col-span-2"
|
||||||
v-model="form.description"
|
v-model="form.description"
|
||||||
:onError="form.errors.description"
|
:onError="form.errors.description"
|
||||||
|
|||||||
@ -59,7 +59,7 @@ const submit = () =>
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
id="Descrición"
|
id="Descripción"
|
||||||
class="col-span-2"
|
class="col-span-2"
|
||||||
v-model="form.description"
|
v-model="form.description"
|
||||||
:onError="form.errors.description"
|
:onError="form.errors.description"
|
||||||
|
|||||||
@ -1,163 +1,169 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { goTo, transl } from "./Component";
|
import { goTo, transl } from "./Component";
|
||||||
import { Link, useForm } from "@inertiajs/vue3";
|
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 PageHeader from "@/Components/Dashboard/PageHeader.vue";
|
||||||
import GoogleIcon from "@/Components/Shared/GoogleIcon.vue";
|
import GoogleIcon from "@/Components/Shared/GoogleIcon.vue";
|
||||||
import DashboardLayout from "@/Layouts/DashboardLayout.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 DepartmentSelector from "@/Components/App/DepartmentSelector.vue";
|
||||||
import MainRoleSelector from "@/Components/App/MainRoleSelector.vue";
|
import MainRoleSelector from "@/Components/App/MainRoleSelector.vue";
|
||||||
import SkillAssignment from "@/Components/App/SkillAssignment.vue";
|
import SkillAssignment from "@/Components/App/SkillAssignment.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
mainRoles: Object,
|
mainRoles: {
|
||||||
skills: Object,
|
type: Array,
|
||||||
scores: Object,
|
default: () => []
|
||||||
|
},
|
||||||
|
skills: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
scores: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
selectedRole: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
selectedDepartment: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Estado del flujo
|
const selectedDepartment = ref(props.selectedDepartment);
|
||||||
const currentStep = ref(1);
|
const selectedMainRole = ref(props.selectedRole);
|
||||||
const selectedDepartment = ref(null);
|
|
||||||
const selectedMainRole = ref(null);
|
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
main_role_id: "",
|
main_role_id: props.selectedRole?.id || '',
|
||||||
skill_id: "",
|
skills: []
|
||||||
scored_id: "",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Datos computados
|
// Obtener departamentos únicos
|
||||||
const uniqueDepartments = computed(() => {
|
const departments = computed(() => {
|
||||||
const deps = [];
|
const uniqueDepartments = [];
|
||||||
const seen = new Set();
|
const seenIds = new Set();
|
||||||
|
|
||||||
props.mainRoles.forEach((role) => {
|
props.mainRoles.forEach(role => {
|
||||||
if (role.department && !seen.has(role.department.id)) {
|
if (role.department && !seenIds.has(role.department.id)) {
|
||||||
seen.add(role.department.id);
|
seenIds.add(role.department.id);
|
||||||
deps.push(role.department);
|
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) => {
|
const handleDepartmentSelect = (department) => {
|
||||||
selectedDepartment.value = department;
|
selectedDepartment.value = department;
|
||||||
currentStep.value = 2;
|
selectedMainRole.value = null;
|
||||||
|
form.main_role_id = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMainRoleSelect = (mainRole) => {
|
const handleRoleSelect = (role) => {
|
||||||
selectedMainRole.value = mainRole;
|
selectedMainRole.value = role;
|
||||||
form.main_role_id = mainRole.id;
|
form.main_role_id = role.id;
|
||||||
currentStep.value = 3;
|
};
|
||||||
|
|
||||||
|
const submit = (skillsData) => {
|
||||||
|
form.skills = skillsData;
|
||||||
|
|
||||||
|
form.post(route(goTo("store")), {
|
||||||
|
onSuccess: () => {
|
||||||
|
// Redireccionar al index
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
// Mostrar mensaje de error
|
||||||
|
},
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
if (currentStep.value === 3) {
|
if (selectedMainRole.value) {
|
||||||
selectedMainRole.value = null;
|
selectedMainRole.value = null;
|
||||||
form.main_role_id = "";
|
form.main_role_id = '';
|
||||||
form.skill_id = "";
|
} else if (selectedDepartment.value) {
|
||||||
form.scored_id = "";
|
|
||||||
currentStep.value = 2;
|
|
||||||
} else if (currentStep.value === 2) {
|
|
||||||
selectedDepartment.value = null;
|
selectedDepartment.value = null;
|
||||||
selectedMainRole.value = null;
|
|
||||||
form.reset();
|
|
||||||
currentStep.value = 1;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetFlow = () => {
|
onMounted(() => {
|
||||||
selectedDepartment.value = null;
|
// Si tenemos rol seleccionado, asegurar que el departamento también esté seleccionado
|
||||||
selectedMainRole.value = null;
|
if (props.selectedRole && props.selectedRole.department) {
|
||||||
form.reset();
|
selectedDepartment.value = props.selectedRole.department;
|
||||||
currentStep.value = 1;
|
selectedMainRole.value = props.selectedRole;
|
||||||
};
|
form.main_role_id = props.selectedRole.id;
|
||||||
|
}
|
||||||
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")),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<DashboardLayout :title="transl('create.title')">
|
<DashboardLayout :title="transl('create.title')">
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<div class="flex items-center space-x-2">
|
<Link :href="route(goTo('index'))">
|
||||||
<Link :href="route(goTo('index'))">
|
<GoogleIcon
|
||||||
<GoogleIcon
|
:title="$t('return')"
|
||||||
:title="$t('return')"
|
class="btn-icon-primary"
|
||||||
class="btn-icon-primary"
|
name="arrow_back"
|
||||||
name="arrow_back"
|
outline
|
||||||
outline
|
/>
|
||||||
/>
|
</Link>
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<!-- Indicador de progreso componentizado -->
|
<div class="w-full pb-8">
|
||||||
<div class="mt-6">
|
<div class="mt-8">
|
||||||
<StepIndicator :current-step="currentStep" />
|
<p v-text="transl('create.description')" />
|
||||||
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Componentes de pasos -->
|
<div class="w-full">
|
||||||
<DepartmentSelector
|
<!-- Navegación hacia atrás -->
|
||||||
v-if="currentStep === 1"
|
<div v-if="selectedDepartment || selectedMainRole" class="mb-6">
|
||||||
:departments="uniqueDepartments"
|
<button
|
||||||
@select="handleDepartmentSelect"
|
@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
|
<!-- Paso 1: Selector de Departamento -->
|
||||||
v-if="currentStep === 2"
|
<DepartmentSelector
|
||||||
:main-roles="mainRoles"
|
v-if="!selectedDepartment"
|
||||||
:selected-department="selectedDepartment"
|
:departments="departments"
|
||||||
@select="handleMainRoleSelect"
|
@select="handleDepartmentSelect"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SkillAssignment
|
<!-- Paso 2: Selector de Rol Principal -->
|
||||||
v-if="currentStep === 3"
|
<MainRoleSelector
|
||||||
:form="form"
|
v-else-if="selectedDepartment && !selectedMainRole"
|
||||||
:skills="skills"
|
:selectedDepartment="selectedDepartment"
|
||||||
:scores="scores"
|
:mainRoles="mainRoles"
|
||||||
:selected-department="selectedDepartment"
|
@select="handleRoleSelect"
|
||||||
:selected-main-role="selectedMainRole"
|
/>
|
||||||
@submit="submit"
|
|
||||||
/>
|
<!-- 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>
|
</DashboardLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,146 +1,91 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { transl, can, goTo } from "./Component";
|
import { transl } from "./Component";
|
||||||
import { ref, computed } from "vue";
|
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 DashboardLayout from "@/Layouts/DashboardLayout.vue";
|
||||||
import DestroyView from "./Destroy.vue";
|
import DepartmentSelector from "@/Components/App/DepartmentSelector.vue";
|
||||||
import EditView from "./Edit.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({
|
const props = defineProps({
|
||||||
mainRoleSkills: Object,
|
mainRoleSkills: Object,
|
||||||
|
departments: Object,
|
||||||
|
mainRoles: Object,
|
||||||
|
skills: Object,
|
||||||
|
scores: Object,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Controladores
|
const selectedDepartment = ref(null);
|
||||||
const Modal = new ModalController();
|
const selectedRole = ref(null);
|
||||||
const Searcher = new SearcherController(goTo("index"));
|
|
||||||
|
|
||||||
// Variables de controladores
|
const filteredMainRoleSkills = computed(() => {
|
||||||
const destroyModal = ref(Modal.destroyModal);
|
if (!props.mainRoleSkills?.data || !selectedRole.value) {
|
||||||
const editModal = ref(Modal.editModal);
|
return [];
|
||||||
const modelModal = ref(Modal.modelModal);
|
}
|
||||||
const query = ref(Searcher.query);
|
|
||||||
|
|
||||||
const groupedRoles = computed(() => {
|
return props.mainRoleSkills.data.filter(roleSkill =>
|
||||||
const groups = {};
|
roleSkill.main_role_id === selectedRole.value.id
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<DashboardLayout :title="transl('system')">
|
<DashboardLayout :title="transl('system')">
|
||||||
<SearcherHead @search="Searcher.search">
|
<PageHeader>
|
||||||
<Link v-if="can('create')" :href="route(goTo('create'))">
|
<button @click="goBack">
|
||||||
<GoogleIcon
|
<GoogleIcon
|
||||||
:title="$t('crud.create')"
|
:title="$t('return')"
|
||||||
class="btn-icon-primary"
|
class="btn-icon-primary"
|
||||||
name="add"
|
name="arrow_back"
|
||||||
outline
|
outline
|
||||||
/>
|
/>
|
||||||
</Link>
|
</button>
|
||||||
</SearcherHead>
|
</PageHeader>
|
||||||
<div class="pt-2 w-full">
|
|
||||||
<Table
|
<!-- Selector de Departamento -->
|
||||||
:items="mainRoleSkills"
|
<DepartmentSelector
|
||||||
@send-pagination="Searcher.searchWithPagination"
|
v-if="!selectedDepartment"
|
||||||
>
|
:departments="departments"
|
||||||
<template #head>
|
@select="handleDepartmentSelect"
|
||||||
<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"
|
|
||||||
/>
|
/>
|
||||||
<DestroyView
|
|
||||||
v-if="can('create')"
|
<!-- Selector de Roles Principales -->
|
||||||
:show="destroyModal"
|
<MainRoleSelector
|
||||||
:model="modelModal"
|
v-if="selectedDepartment"
|
||||||
@close="Modal.switchDestroyModal"
|
:selectedDepartment="selectedDepartment"
|
||||||
|
:mainRoles="mainRoles"
|
||||||
|
@select="handleRoleSelect"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SkillRole
|
||||||
|
v-if="selectedRole"
|
||||||
|
:mainRoleSkills="filteredMainRoleSkills"
|
||||||
|
:selectedRole="selectedRole"
|
||||||
|
@select="handleSkillSelect"
|
||||||
/>
|
/>
|
||||||
</DashboardLayout>
|
</DashboardLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -59,7 +59,7 @@ const submit = () =>
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
id="Descrición"
|
id="Descripción"
|
||||||
class="col-span-2"
|
class="col-span-2"
|
||||||
v-model="form.description"
|
v-model="form.description"
|
||||||
:onError="form.errors.description"
|
:onError="form.errors.description"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user