Habilidades puntuadas terminado

This commit is contained in:
Juan Felipe Zapata Moreno 2025-07-17 13:48:50 -06:00
parent 24a1e18dc3
commit d96348bbf6
10 changed files with 198 additions and 128 deletions

View File

@ -1,4 +0,0 @@
{
"tabWidth": 2,
"useTabs": false
}

View File

@ -43,6 +43,7 @@ public function index()
->orWhereIn('scored_id', $scores)
->with([
'mainRole:id,name,department_id',
'mainRole.department:id,name',
'skill:id,name',
'score:id,alias'
])
@ -95,7 +96,6 @@ public function create()
public function store(StoreMainRoleSkills $request)
{
try {
$create = [];
foreach ($request['skills'] as $skill) {
$create[] = [
@ -110,31 +110,42 @@ public function store(StoreMainRoleSkills $request)
MainRoleSkills::insert($create);
return $this->index();
} catch (\Illuminate\Database\QueryException $e) {
// Si hay error de restricción única
if ($e->getCode() === '23000') {
return back()->withErrors(['skills' => 'Una o más habilidades ya están asignadas a este rol.']);
}
throw $e;
}
}
public function update(UpdateMainRoleSkills $request, MainRoleSkills $mainRoleSkills)
public function edit(MainRoleSkills $mainRoleSkills)
{
$mainRoleSkills->update($request->all());
$mainRoleSkills->load(['mainRole.department', 'skill.department', 'score']);
$scores = Score::orderBy('alias', 'ASC')->get();
return $this->vuew('edit', [
'mainRoleSkills' => $mainRoleSkills,
'scores' => $scores,
]);
}
public function destroy($mainRoleId)
public function update(UpdateMainRoleSkills $request, $id)
{
$mainRoleSkills = MainRoleSkills::findOrFail($id);
$mainRoleSkills->update([
'scored_id' => $request->scored_id,
]);
return redirect()->back();
}
public function destroy($id)
{
try {
// Eliminar todas las habilidades asociadas a este rol principal
MainRoleSkills::where('main_role_id', $mainRoleId)->delete();
$mainRoleSkills = MainRoleSkills::findOrFail($id);
$mainRoleSkills->delete();
return $this->index();
} catch (\Throwable $th) {
Log::channel('mainRoleSkills')->error($th->getMessage());
return response()->json(['error' => 'Error al eliminar las habilidades'], 500);
Log::error($th->getMessage());
return response()->json(['error' => 'Error al eliminar las habilidades']);
}
}
}

View File

@ -32,8 +32,6 @@ public function authorize()
public function rules()
{
return [
'main_role_id' => ['required', 'integer'],
'skill_id' => ['required', 'integer'],
'scored_id' => ['required', 'integer'],
];
}

View File

@ -1,22 +1,34 @@
<script setup>
import { computed } from "vue";
import { computed, ref } from "vue";
import { Link } from "@inertiajs/vue3";
import { can, goTo } from "@/Pages/Admin/MainRoleSkills/Component";
import GoogleIcon from "@/Components/Shared/GoogleIcon.vue";
import ModalController from "@/Controllers/ModalController.js";
import DestroyView from "@/Pages/Admin/MainRoleSkills/Destroy.vue";
import EditView from '@/Pages/Admin/MainRoleSkills/Edit.vue';
const emit = defineEmits(["select"]);
const props = defineProps({
mainRoleSkills: Array,
selectedRole: Object,
scores: {
type: Array,
default: () => []
}
});
// Controlador de modal
const Modal = new ModalController();
const destroyModal = ref(Modal.destroyModal);
const editModal = ref(Modal.editModal);
const modelModal = ref(Modal.modelModal);
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
);
@ -32,10 +44,13 @@ const roleSkills = computed(() => {
</h2>
<Link
v-if="can('create')"
:href="route(goTo('create'), {
:href="
route(goTo('create'), {
role_id: selectedRole.id,
department_id: selectedRole.department?.id || selectedRole.department_id
})"
department_id:
selectedRole.department?.id || selectedRole.department_id,
})
"
>
<GoogleIcon
:title="$t('crud.create')"
@ -75,6 +90,22 @@ const roleSkills = computed(() => {
>
{{ roleSkill.skill.description }}
</p>
<div class="flex items-center justify-end mt-4 gap-2">
<GoogleIcon
v-if="can('edit')"
:title="$t('crud.edit')"
class="btn-icon-danger w-6 h-6 bg-blue-500 text-white rounded hover:bg-blue-600"
name="edit"
@click.stop="Modal.switchEditModal(roleSkill)"
/>
<GoogleIcon
v-if="can('destroy')"
:title="$t('crud.destroy')"
class="btn-icon-danger w-6 h-6 bg-red-500 text-white rounded hover:bg-red-600"
name="delete"
@click.stop="Modal.switchDestroyModal(roleSkill)"
/>
</div>
</div>
</div>
@ -86,5 +117,19 @@ const roleSkills = computed(() => {
Las habilidades se pueden asignar desde la sección de creación
</p>
</div>
<EditView
v-if="can('edit')"
:show="editModal"
:model="modelModal"
:scores="scores"
@close="Modal.switchEditModal"
@switchModal="Modal.switchEditModal"
/>
<DestroyView
v-if="can('destroy')"
:show="destroyModal"
:model="modelModal"
@close="Modal.switchDestroyModal"
/>
</div>
</template>

View File

@ -46,7 +46,7 @@ const onRemove = () => {
</script>
<template>
<div class="flex flex-col">
<div class="flex flex-col relative">
<label v-if="title" class="block mb-2 text-sm font-medium text-gray-900">
{{title}} <span class="text-red-500" v-if="required">*</span> <slot name="label-icon" />
</label>

View File

@ -26,7 +26,7 @@ const props = defineProps({
</template>
<template #content>
<div class="w-full right-0 mt-2">
<div class="rounded overflow-hidden">
<div class="rounded">
<slot />
</div>
</div>

View File

@ -36,8 +36,7 @@ const destroy = (id) => router.delete(route(goTo('destroy'), {id}), {
@destroy="destroy(model.id)"
>
<Header
:title="model.name"
:subtitle="model.description"
:title="model.skill.name"
/>
</DestroyModal>
</template>

View File

@ -1,64 +1,84 @@
<script setup>
import { goTo } from './Component';
import { useForm } from '@inertiajs/vue3';
import { onUpdated } from 'vue';
import { goTo } from "./Component";
import { router, useForm } from "@inertiajs/vue3";
import Input from '@/Components/Dashboard/Form/Input.vue';
import EditModal from '@/Components/Dashboard/Modal/Edit.vue';
import Header from '@/Components/Dashboard/Modal/Elements/Header.vue';
import Selectable from "@/Components/Dashboard/Form/Selectable.vue";
import EditModal from "@/Components/Dashboard/Modal/Edit.vue";
import Header from "@/Components/Dashboard/Modal/Elements/Header.vue";
const emit = defineEmits([
'close',
'switchModal'
]);
const emit = defineEmits(["close", "switchModal"]);
const props = defineProps({
show: Boolean,
model: Object
model: Object,
scores: Array,
});
const form = useForm({});
const form = useForm({
scored_id: "",
});
const update = (id) => {
form.transform(data => ({
...props.model
})).put(route(goTo('update'), {id}),{
form
.transform((data) => ({
scored_id:
typeof data.scored_id === "object" ? data.scored_id.id : data.scored_id,
}))
.put(route(goTo("update"), { id }), {
preserveScroll: true,
onSuccess: () => {
Notify.success(lang('updated'))
emit('switchModal')
}
Notify.success(lang("updated"));
emit("switchModal");
router.reload();
},
});
}
};
</script>
<template>
<EditModal
:show="show"
@close="$emit('close')"
@update="update(model.id)"
>
<Header
:title="model.name"
/>
<EditModal :show="show" @close="$emit('close')" @update="update(model.id)">
<Header :title="model.main_role?.name" />
<div class="py-2 border-b">
<div class="p-4">
<form>
<div class="grid gap-6 mb-6 lg:grid-cols-2">
<Input
id="Nombre"
placeholder="name"
v-model="model.name"
:onError="form.errors.name"
required
/>
<Input
id="Descripción"
v-model="model.description"
:onError="form.errors.description"
<div class="grid gap-6 mb-6 overflow-auto">
<div class="bg-gray-50 p-4 rounded-lg">
<h4 class="bg-gray-50 text-gray-700 mb-2 font-medium">
Información:
</h4>
<div class="text-sm text-gray-600 space-y-1">
<p>
<span class="font-medium">Rol: </span
>{{ model.main_role?.name }}
</p>
<p>
<span class="font-medium">Departamento: </span>
{{ model.main_role?.department?.name }}
</p>
<p>
<span class="font-medium">Habilidad: </span
>{{ model.skill?.name }}
</p>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">
Cambiar Puntuación
</label>
<Selectable
id="scored_id"
v-model="form.scored_id"
:options="scores"
:onError="form.errors.scored_id"
label="alias"
track-by="id"
required
/>
<p class="text-xs text-gray-500 mt-1">
Puntuación actual:
<span class="font-medium"> {{ model.score?.alias }}</span>
</p>
</div>
</div>
</form>
</div>

View File

@ -85,6 +85,7 @@ const goBack = () => {
v-if="selectedRole"
:mainRoleSkills="filteredMainRoleSkills"
:selectedRole="selectedRole"
:scores="scores"
@select="handleSkillSelect"
/>
</DashboardLayout>