Terminar Habilidades puntuadas

This commit is contained in:
Juan Felipe Zapata Moreno 2025-07-11 16:29:49 -06:00
parent d54c017d40
commit 776ef80b93
12 changed files with 384 additions and 342 deletions

View File

@ -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);
}
}
} }

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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>

View File

@ -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: {

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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"