Compare commits

..

No commits in common. "df8a4c258a542294cc40462ee097fa9df6766b84" and "be89d18e74c6514e01bcc1f54bbe7bbbede582e8" have entirely different histories.

61 changed files with 228 additions and 3807 deletions

View File

@ -1,72 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use App\Http\Requests\StoreDepartment;
use App\Http\Requests\UpdateDepartment;
use App\Models\department;
use Notsoweb\Core\Http\Controllers\VueController;
/**
* Descripción
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class DepartmentController extends VueController
{
public function __construct()
{
$this->vueRoot('admin.departments');
}
public function index()
{
$q = request()->get('q');
return $this->vuew('index', [
'departments' => department::where('name', 'LIKE', "%{$q}%")
->select([
'id',
'name',
'description',
])
->paginate(config('app.pagination'))
]);
}
public function create()
{
return $this->vuew('create');
}
public function store(StoreDepartment $request)
{
$data = $request->all();
department::create($data);
return $this->index();
}
public function update(UpdateDepartment $request, $department)
{
$data = $request->all();
department::find($department)->update($data);
}
public function destroy($department)
{
department::find($department)->delete();
return $this->index();
}
}

View File

@ -1,74 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use App\Http\Requests\StoreMainRole;
use App\Http\Requests\UpdateMainRole;
use App\Models\department;
use App\Models\mainRole;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Notsoweb\Core\Http\Controllers\VueController;
/**
* Descripción
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class MainRoleController extends VueController
{
public function __construct()
{
$this->vueRoot('admin.mainRole');
}
public function index()
{
$q = request()->get('q');
$mainRoles = mainRole::orderBy('name', 'ASC')
->where('name', 'LIKE', "%{$q}%")
->with('department:id,name')
->paginate(config('app.pagination'));
return $this->vuew('index', [
'mainRoles' => $mainRoles,
]);
}
public function create()
{
$department = department::orderBy('name', 'ASC')->get();
return $this->vuew('create', [
'departments' => $department,
]);
}
public function store(StoreMainRole $request)
{
mainRole::create($request->all());
return $this->index();
}
public function update(UpdateMainRole $request, mainRole $mainRole)
{
$mainRole->update($request->all());
}
public function destroy(mainRole $mainRole)
{ try{
$mainRole = mainRole::find($mainRole);
$mainRole->delete();
}catch (\Throwable $th) {
Log::channel('mainRole')->error($th->getMessage());
}
}
}

View File

@ -1,98 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use App\Http\Requests\StoreMainRoleSkills;
use App\Http\Requests\UpdateMainRoleSkills;
use App\Models\mainRole;
use App\Models\MainRoleSkills;
use App\Models\Score;
use App\Models\Skill;
use Illuminate\Support\Facades\Log;
use Notsoweb\Core\Http\Controllers\VueController;
/**
* Descripción
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class MainRoleSkillsController extends VueController
{
public function __construct()
{
return $this->vueRoot('admin.mainRoleSkills');
}
public function index()
{
$q = request()->get('q');
$mainRole = mainRole::where('name', 'LIKE', "%{$q}%")->pluck('id');
$skills = Skill::where('name', 'LIKE', "%{$q}%")->pluck('id');
$scores = Score::where('alias', 'LIKE', "%{$q}%")->pluck('id');
$mainRoleSkills = MainRoleSkills::whereIn('main_role_id', $mainRole)
->orWhereIn('skill_id', $skills)
->orWhereIn('scored_id', $scores)
->with([
'mainRole:id,name,department_id',
'mainRole.department:id,name,description',
'skill:id,name,department_id',
'score:id,alias'
])
->paginate(config('app.pagination'));
return $this->vuew('index', [
'mainRoleSkills' => $mainRoleSkills
]);
}
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();
return $this->vuew('create', [
'mainRoles' => $mainRoles,
'skills' => $skills,
'scores' => $scores
]);
}
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(),
];
}
MainRoleSkills::insert($create);
return $this->index();
}
public function update(UpdateMainRoleSkills $request, MainRoleSkills $mainRoleSkills)
{
$mainRoleSkills->update($request->all());
}
public function destroy($id)
{
$mainRoleSkill = MainRoleSkills::findOrFail($id);
$mainRoleSkill->delete();
}
}

View File

@ -1,71 +0,0 @@
<?php namespace App\Http\Controllers\Admin;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use App\Models\Score;
use App\Http\Requests\StoreScore;
use App\Http\Requests\UpdateScore;
use Notsoweb\Core\Http\Controllers\VueController;
/**
* Descripción
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class ScoreController extends VueController
{
public function __construct()
{
$this->vueRoot('admin.app');
}
public function index()
{
$q = request()->get('q');
return $this->vuew('index', [
'scores' => Score::where('alias', 'LIKE', "%{$q}%")
->orWhere('value', 'LIKE', "%{$q}%")
->select([
'id',
'alias',
'value',
'description',
])
->paginate(config('app.pagination'))
]);
}
public function create()
{
return $this->vuew('create');
}
public function store(StoreScore $request)
{
$data = $request->all();
Score::create($data);
return $this->index();
}
public function update(UpdateScore $request, $score)
{
$data = $request->all();
Score::find($score)->update($data);
}
public function destroy($score)
{
Score::find($score)->delete();
return $this->index();
}
}

View File

@ -1,65 +0,0 @@
<?php namespace App\Http\Controllers\Admin;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use App\Http\Requests\StoreSkill;
use App\Http\Requests\UpdateSkill;
use App\Models\department;
use App\Models\Skill;
use Notsoweb\Core\Http\Controllers\VueController;
/**
* Descripción
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class SkillController extends VueController
{
public function __construct()
{
return $this->vueRoot('admin.skills');
}
public function index()
{
$q = request()->get('q');
$skills = Skill::orderBy('name', 'ASC')
->where('name', 'LIKE', "%{$q}%")
->with('department:id,name')
->paginate(config('app.pagination'));
return $this->vuew('index', [
'skills' => $skills,
]);
}
public function create()
{
$department = department::orderBy('name', 'ASC')->get();
return $this->vuew('create', [
'departments' => $department,
]);
}
public function store(StoreSkill $request)
{
Skill::create($request->all());
return $this->index();
}
public function update(UpdateSkill $request, Skill $skill)
{
$skill->update($request->all());
}
public function destroy (Skill $skill)
{
$skill->delete();
}
}

View File

@ -1,39 +0,0 @@
<?php namespace App\Http\Requests;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use Illuminate\Foundation\Http\FormRequest;
/**
* Valida el almacenamiento de un usuario
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class StoreDepartment extends FormRequest
{
/**
* Determinar si el usuario esta autorizado
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Reglas de validación
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'name' => ['required', 'string'],
'description' => ['nullable', 'string'],
];
}
}

View File

@ -1,40 +0,0 @@
<?php namespace App\Http\Requests;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use Illuminate\Foundation\Http\FormRequest;
/**
* Valida el almacenamiento de un usuario
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class StoreMainRole extends FormRequest
{
/**
* Determinar si el usuario esta autorizado
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Reglas de validación
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'name' => ['required', 'string'],
'description' => ['nullable', 'string'],
'department_id' => ['required', 'integer'],
];
}
}

View File

@ -1,41 +0,0 @@
<?php namespace App\Http\Requests;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use Illuminate\Foundation\Http\FormRequest;
/**
* Valida el almacenamiento de un usuario
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class StoreMainRoleSkills extends FormRequest
{
/**
* Determinar si el usuario esta autorizado
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Reglas de validación
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'main_role_id' => ['required', 'integer'],
'skills' => ['required', 'array', 'min:1'],
'skills.*.skill_id' => ['required', 'integer'],
'skills.*.scored_id' => ['required', 'integer'],
];
}
}

View File

@ -1,40 +0,0 @@
<?php namespace App\Http\Requests;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use Illuminate\Foundation\Http\FormRequest;
/**
* Valida el almacenamiento de un usuario
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class StoreScore extends FormRequest
{
/**
* Determinar si el usuario esta autorizado
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Reglas de validación
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'alias' => ['required', 'string'],
'value' => ['required', 'string'],
'description' => ['nullable', 'string'],
];
}
}

View File

@ -1,40 +0,0 @@
<?php namespace App\Http\Requests;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use Illuminate\Foundation\Http\FormRequest;
/**
* Valida el almacenamiento de un usuario
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class StoreSkill extends FormRequest
{
/**
* Determinar si el usuario esta autorizado
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Reglas de validación
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'name' => ['required', 'string'],
'description' => ['nullable', 'string'],
'department_id' => ['required', 'integer'],
];
}
}

View File

@ -1,40 +0,0 @@
<?php namespace App\Http\Requests;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
/**
* Valida la actualización de los usuarios
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class UpdateDepartment extends FormRequest
{
/**
* Determinar si el usuario esta autorizado
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Reglas de validación
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'name' => ['required', 'string'],
'description' => ['nullable', 'string'],
];
}
}

View File

@ -1,40 +0,0 @@
<?php namespace App\Http\Requests;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use Illuminate\Foundation\Http\FormRequest;
/**
* Valida el almacenamiento de un usuario
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class UpdateMainRole extends FormRequest
{
/**
* Determinar si el usuario esta autorizado
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Reglas de validación
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'name' => ['required', 'string'],
'description' => ['nullable', 'string'],
'department_id' => ['required', 'integer','exists:departments,id'],
];
}
}

View File

@ -1,40 +0,0 @@
<?php namespace App\Http\Requests;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use Illuminate\Foundation\Http\FormRequest;
/**
* Valida el almacenamiento de un usuario
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class UpdateMainRoleSkills extends FormRequest
{
/**
* Determinar si el usuario esta autorizado
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Reglas de validación
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'main_role_id' => ['required', 'integer'],
'skill_id' => ['required', 'integer'],
'scored_id' => ['required', 'integer'],
];
}
}

View File

@ -1,41 +0,0 @@
<?php namespace App\Http\Requests;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
/**
* Valida la actualización de los usuarios
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class UpdateScore extends FormRequest
{
/**
* Determinar si el usuario esta autorizado
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Reglas de validación
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'alias' => ['required', 'string'],
'value' => ['required', 'string'],
'description' => ['nullable', 'string'],
];
}
}

View File

@ -1,40 +0,0 @@
<?php namespace App\Http\Requests;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use Illuminate\Foundation\Http\FormRequest;
/**
* Valida el almacenamiento de un usuario
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class UpdateSkill extends FormRequest
{
/**
* Determinar si el usuario esta autorizado
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Reglas de validación
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'name' => ['required', 'string'],
'description' => ['nullable', 'string'],
'department_id' => ['required', 'integer','exists:departments,id'],
];
}
}

View File

@ -1,45 +0,0 @@
<?php namespace App\Models;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use App\Http\Traits\ModelExtend;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* Descripción
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class MainRoleSkills extends Model
{
use HasFactory,
ModelExtend;
/**
* Atributos llenables masivamente
*/
protected $fillable = [
'main_role_id',
'skill_id',
'scored_id',
];
public function mainRole()
{
return $this->belongsTo(mainRole::class, 'main_role_id');
}
public function skill()
{
return $this->belongsTo(Skill::class, 'skill_id');
}
public function score()
{
return $this->belongsTo(Score::class, 'scored_id');
}
}

View File

@ -1,35 +0,0 @@
<?php namespace App\Models;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use App\Http\Traits\ModelExtend;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* Descripción
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class Score extends Model
{
use HasFactory,
ModelExtend;
/**
* Atributos llenables masivamente
*/
protected $fillable = [
'alias',
'value',
'description',
];
public function mainRoleSkills()
{
return $this->hasMany(MainRoleSkills::class);
}
}

View File

@ -1,46 +0,0 @@
<?php namespace App\Models;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use App\Http\Traits\ModelExtend;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* Descripción
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class Skill extends Model
{
use HasFactory,
ModelExtend;
/**
* Atributos llenables masivamente
*/
protected $fillable = [
'name',
'description',
'department_id',
];
public function department()
{
return $this->belongsTo(department::class);
}
public function mainRoles()
{
return $this->belongsTo(MainRole::class);
}
public function mainRoleSkills()
{
return $this->hasMany(MainRoleSkills::class);
}
}

View File

@ -1,42 +0,0 @@
<?php namespace App\Models;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use App\Http\Traits\ModelExtend;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* Descripción
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class department extends Model
{
use HasFactory,
ModelExtend;
/**
* Atributos llenables masivamente
*/
protected $fillable = [
'name',
'description',
];
/**
* Un departamento tiene muchos main roles
*/
public function mainRoles()
{
return $this->hasMany(MainRole::class);
}
public function skills()
{
return $this->hasMany(Skill::class);
}
}

View File

@ -1,53 +0,0 @@
<?php namespace App\Models;
/**
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
*/
use App\Http\Traits\ModelExtend;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* Roles principales del sistema
*
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
*
* @version 1.0.0
*/
class MainRole extends Model
{
use HasFactory,
ModelExtend;
/**
* Nombre de la tabla
*/
protected $table = 'main_roles';
/**
* Atributos llenables masivamente
*/
protected $fillable = [
'name',
'description',
'department_id'
];
/**
* Un main role pertenece a un departamento
*/
public function department()
{
return $this->belongsTo(department::class);
}
public function skills()
{
return $this->hasMany(Skill::class);
}
public function mainRoleSkills()
{
return $this->hasMany(MainRoleSkills::class);
}
}

View File

@ -1,34 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('scores', function (Blueprint $table){
$table->id();
$table->string('alias');
$table->integer('value');
$table->string('description')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('scores');
}
};

View File

@ -1,29 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('departments', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('description')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('departments');
}
};

View File

@ -1,30 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('main_roles', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('description')->nullable();
$table->foreignId('department_id')->constrained('departments')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('main_roles');
}
};

View File

@ -1,30 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('skills', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('description');
$table->foreignId('department_id')->constrained('departments')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('skills');
}
};

View File

@ -1,30 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('main_role_skills', function (Blueprint $table) {
$table->id();
$table->foreignId('main_role_id')->constrained('main_roles')->onDelete('cascade');
$table->foreignId('skill_id')->constrained('skills')->onDelete('cascade');
$table->foreignId('scored_id')->constrained('scores')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('main_role_skills');
}
};

2
package-lock.json generated
View File

@ -1,5 +1,5 @@
{ {
"name": "Scores", "name": "template-laravel-vuejs",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {

View File

@ -1,34 +0,0 @@
<script setup>
defineEmits(['select']);
const props = defineProps({
departments: Object,
});
console.log('Department props:', props.departments);
</script>
<template>
<div class="w-full">
<div class="mb-6">
<h2 class="text-xl font-semibold mb-2">{{ $t('department.select.title') }}</h2>
<p class="text-gray-600">{{ $t('department.select.description') }}</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<button
v-for="department in departments"
:key="department.id"
@click="$emit('select', department)"
class="group p-6 border-2 border-gray-200 rounded-lg hover:border-primary hover:bg-primary/5 transition-all duration-200 text-left focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
>
<h3 class="font-semibold text-lg mb-2 group-hover:text-primary transition-colors">
{{ department.name }}
</h3>
<p class="text-gray-600 text-sm">
{{ department.description }}
</p>
</button>
</div>
</div>
</template>

View File

@ -1,53 +0,0 @@
<script setup>
import { computed } from 'vue';
const emit = defineEmits(['select']);
const props = defineProps({
mainRoles: {
type: Object,
required: true
},
selectedDepartment: {
type: Object,
required: true
}
});
const departmentMainRoles = computed(() => {
return props.mainRoles.filter(role =>
role.department && role.department.id === props.selectedDepartment.id
);
});
</script>
<template>
<div class="w-full">
<div class="mb-6">
<h2 class="text-xl font-semibold mb-2">
{{ $t('mainRole.inDepartment', { department: selectedDepartment.name }) }}
</h2>
<p class="text-gray-600">{{ $t('mainRole.select.description') }}</p>
</div>
<div v-if="departmentMainRoles.length > 0" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<button
v-for="mainRole in departmentMainRoles"
:key="mainRole.id"
@click="$emit('select', mainRole)"
class="group p-6 border-2 border-gray-200 rounded-lg hover:border-primary hover:bg-primary/5 transition-all duration-200 text-left focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
>
<h3 class="font-semibold text-lg mb-2 group-hover:text-primary transition-colors">
{{ mainRole.name }}
</h3>
<p class="text-gray-600 text-sm">
{{ mainRole.description}}
</p>
</button>
</div>
<div v-else class="text-center py-12">
<p class="text-gray-500 text-lg">{{ $t('mainRole.noRolesInDepartment') }}</p>
</div>
</div>
</template>

View File

@ -1,191 +0,0 @@
<script setup>
import { ref, computed, onMounted, watch } 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 props = defineProps({
form: {
type: Object,
required: true,
},
skills: {
type: Object,
required: true,
},
scores: {
type: Object,
required: true,
},
selectedDepartment: {
type: Object,
required: true,
},
selectedMainRole: {
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 selectedScore = props.scores.find(
(score) => score.id === scoredId.value.id || score.id === scoredId.value
);
if (selectedSkill && selectedScore) {
const newItem = {
skill_id: selectedSkill.id,
scored_id: selectedScore.id,
skill_name: selectedSkill.name,
score_alias: selectedScore.alias,
};
todos.value.push(newItem);
// Limpiar los campos
skillId.value = "";
scoredId.value = "";
// Emitir el arreglo actualizado
emit("update:modelValue", todos.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;
});
const submitForm = () => {
if (isFormValid.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>
<template>
<div class="w-full">
<div class="mb-6">
<h2 class="text-xl font-semibold mb-2">
{{ $t("skill.assignTo", { role: selectedMainRole.name }) }}
</h2>
<p class="text-gray-600">
{{
$t("skill.assignment.description", {
department: selectedDepartment.name,
})
}}
</p>
</div>
<!-- Formulario para agregar habilidades -->
<div class="bg-white rounded-lg shadow-sm border p-6 mb-6">
<div class="grid gap-6 grid-cols-1 md:grid-cols-2">
<Selectable
id="skill_id"
v-model="skillId"
:options="departmentSkills"
:title="$t('skill.title')"
placeholder="Selecciona una habilidad..."
label="name"
track-by="id"
required
/>
<Selectable
id="scored_id"
v-model="scoredId"
:options="scores"
:title="$t('scores.title')"
placeholder="Selecciona una puntuación..."
label="alias"
track-by="id"
required
/>
</div>
<div class="flex justify-center mt-6">
<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"
>
Agregar Habilidad
</button>
</div>
</div>
<!-- Lista de habilidades agregadas -->
<div class="space-y-3 mb-6">
<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>
<div
@click="removeTodo(index)"
class="rounded-lg px-4 py-2 bg-red-600 text-white cursor-pointer hover:bg-red-700 transition-colors"
>
Quitar
</div>
</div>
</template>
<div v-if="todos.length === 0" class="text-gray-500 text-center py-8 bg-gray-50 rounded-lg">
No hay habilidades agregadas
</div>
</div>
<!-- Botón de envío -->
<div class="flex justify-center">
<PrimaryButton
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing || !isFormValid"
type="button"
@click="submitForm"
>
<span>{{ $t("Crear") }}</span>
</PrimaryButton>
</div>
</div>
</template>

View File

@ -1,38 +0,0 @@
<script setup>
defineProps({
currentStep: {
type: Number,
required: true
}
});
const steps = [
{ number: 1, name: 'department.title' },
{ number: 2, name: 'mainRole.title' },
{ number: 3, name: 'skill.title' }
];
</script>
<template>
<div class="w-full pb-4">
<div class="flex items-center justify-center space-x-4 mb-6">
<template v-for="(step, index) in steps" :key="step.number">
<div class="flex items-center">
<div
class="w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium transition-colors"
:class="currentStep >= step.number ? 'bg-primary text-primary-on' : 'bg-gray-300 text-gray-600'"
>
{{ step.number }}
</div>
<span class="ml-2 text-sm font-medium">{{ $t(step.name) }}</span>
</div>
<div
v-if="index < steps.length - 1"
class="w-8 h-1 bg-gray-300 transition-colors"
:class="currentStep > step.number ? 'bg-primary' : ''"
/>
</template>
</div>
</div>
</template>

View File

@ -1,317 +1,228 @@
export default { export default {
"&": "y", '&':'y',
account: { account: {
delete: { delete: {
confirm: confirm:'¿Está seguro de que quiere eliminar su cuenta? Una vez eliminada su cuenta, todos sus recursos y datos se borrarán permanentemente. Por favor, introduzca su contraseña para confirmar que desea eliminar permanentemente su cuenta.',
"¿Está seguro de que quiere eliminar su cuenta? Una vez eliminada su cuenta, todos sus recursos y datos se borrarán permanentemente. Por favor, introduzca su contraseña para confirmar que desea eliminar permanentemente su cuenta.", description:'Eliminar permanentemente su cuenta.',
description: "Eliminar permanentemente su cuenta.", onDelete:'Una vez eliminada su cuenta, todos sus recursos y datos se borrarán permanentemente. Antes de eliminar su cuenta, descargue los datos o la información que desee conservar.',
onDelete: title:'Eliminar cuenta',
"Una vez eliminada su cuenta, todos sus recursos y datos se borrarán permanentemente. Antes de eliminar su cuenta, descargue los datos o la información que desee conservar.",
title: "Eliminar cuenta",
}, },
email: { email: {
notifySendVerification: notifySendVerification:'Se ha enviado un nuevo enlace de verificación a su dirección de correo electrónico.',
"Se ha enviado un nuevo enlace de verificación a su dirección de correo electrónico.", sendVerification:'Haga clic aquí para volver a enviar el correo electrónico de verificación.',
sendVerification: unverify: 'Su dirección de correo electrónico no está verificada.',
"Haga clic aquí para volver a enviar el correo electrónico de verificación.",
unverify: "Su dirección de correo electrónico no está verificada.",
}, },
manage: "Administrar cuenta", manage:'Administrar cuenta',
password: { password: {
description: description:'Asegúrese de que su cuenta utiliza una contraseña larga y aleatoria para estar seguro.',
"Asegúrese de que su cuenta utiliza una contraseña larga y aleatoria para estar seguro.", new:'Nueva contraseña',
new: "Nueva contraseña", reset:'Restaurar contraseña',
reset: "Restaurar contraseña", secure:'Esta es una zona segura de la aplicación. Confirme su contraseña antes de continuar.',
secure: update: 'Actualizar contraseña',
"Esta es una zona segura de la aplicación. Confirme su contraseña antes de continuar.", verify:'Por su seguridad, confirme su contraseña para continuar.',
update: "Actualizar contraseña",
verify: "Por su seguridad, confirme su contraseña para continuar.",
}, },
profile: { profile: {
description: description:'Actualice la información del perfil de su cuenta y su dirección de correo electrónico.',
"Actualice la información del perfil de su cuenta y su dirección de correo electrónico.", title:'Información del perfil',
title: "Información del perfil",
}, },
sessions: { sessions: {
confirm: confirm:'Por favor, introduzca su contraseña para confirmar que desea salir de sus otras sesiones de navegación en todos sus dispositivos.',
"Por favor, introduzca su contraseña para confirmar que desea salir de sus otras sesiones de navegación en todos sus dispositivos.", description: 'Gestiona y cierra tus sesiones activas en otros navegadores y dispositivos.',
description: last:'Último activo',
"Gestiona y cierra tus sesiones activas en otros navegadores y dispositivos.", logout:'Cerrar otras sesiones del navegador',
last: "Último activo", onLogout:'Si es necesario, puede cerrar la sesión de todos sus otros navegadores en todos sus dispositivos. A continuación se enumeran algunas de sus sesiones recientes; sin embargo, esta lista puede no ser exhaustiva. Si crees que tu cuenta ha sido comprometida, también deberías actualizar tu contraseña.',
logout: "Cerrar otras sesiones del navegador", this: 'Dispositivo actual',
onLogout: title: 'Sesiones del navegador',
"Si es necesario, puede cerrar la sesión de todos sus otros navegadores en todos sus dispositivos. A continuación se enumeran algunas de sus sesiones recientes; sin embargo, esta lista puede no ser exhaustiva. Si crees que tu cuenta ha sido comprometida, también deberías actualizar tu contraseña.",
this: "Dispositivo actual",
title: "Sesiones del navegador",
}, },
twoFactor: { twoFactor: {
codes:{ codes:{
regenerate: "Regenerar los códigos de recuperación", regenerate:'Regenerar los códigos de recuperación',
show: "Mostrar códigos de recuperación", show:'Mostrar códigos de recuperación',
store: store:'Guarde estos códigos de recuperación en un gestor de contraseñas seguro. Pueden utilizarse para recuperar el acceso a su cuenta si se pierde su dispositivo de autenticación de dos factores.',
"Guarde estos códigos de recuperación en un gestor de contraseñas seguro. Pueden utilizarse para recuperar el acceso a su cuenta si se pierde su dispositivo de autenticación de dos factores.",
}, },
description: description:'Añada seguridad adicional a su cuenta mediante la autenticación de dos factores.',
"Añada seguridad adicional a su cuenta mediante la autenticación de dos factores.", isEnable:'Ha activado la autenticación de dos factores.',
isEnable: "Ha activado la autenticación de dos factores.",
isNotEnable:{ isNotEnable:{
title: "No ha activado la autenticación de dos factores.", title:'No ha activado la autenticación de dos factores.',
description: description:'Cuando la autenticación de dos factores está activada, se le pedirá un token seguro y aleatorio durante la autenticación. Puedes recuperar este token desde la aplicación Google Authenticator de tu teléfono.',
"Cuando la autenticación de dos factores está activada, se le pedirá un token seguro y aleatorio durante la autenticación. Puedes recuperar este token desde la aplicación Google Authenticator de tu teléfono.",
}, },
key: "Llave de configuración", key:'Llave de configuración',
login: { login: {
onAuth: onAuth: 'Por favor, confirme el acceso a su cuenta introduciendo el código de autentificación proporcionado por su aplicación de autentificación.',
"Por favor, confirme el acceso a su cuenta introduciendo el código de autentificación proporcionado por su aplicación de autentificación.", onRecovery: 'Confirme el acceso a su cuenta introduciendo uno de sus códigos de recuperación de emergencia.',
onRecovery:
"Confirme el acceso a su cuenta introduciendo uno de sus códigos de recuperación de emergencia.",
}, },
onFinish: "Termina de habilitar la autenticación de dos factores.", onFinish:'Termina de habilitar la autenticación de dos factores.',
qr: { qr: {
isConfirmed: isConfirmed: 'La autenticación de dos factores ya está activada. Escanee el siguiente código QR con la aplicación de autenticación de su teléfono o introduzca la clave de configuración.',
"La autenticación de dos factores ya está activada. Escanee el siguiente código QR con la aplicación de autenticación de su teléfono o introduzca la clave de configuración.", onConfirmed: 'Para terminar de habilitar la autenticación de dos factores, escanea el siguiente código QR utilizando la aplicación de autenticación de tu teléfono o introduce la clave de configuración y proporciona el código OTP generado.',
onConfirmed:
"Para terminar de habilitar la autenticación de dos factores, escanea el siguiente código QR utilizando la aplicación de autenticación de tu teléfono o introduce la clave de configuración y proporciona el código OTP generado.",
}, },
recovery: { recovery: {
code: "Código de recuperación", code: 'Código de recuperación',
useAuth: "Utilizar un código de autentificación", useAuth: 'Utilizar un código de autentificación',
useCode: "Utiliza un código de recuperación", useCode: 'Utiliza un código de recuperación',
}, },
title: "Autenticación de dos factores", title:'Autenticación de dos factores',
}, },
}, },
actions: "Acciones", actions:'Acciones',
auth: { auth: {
forgotPassword: { forgotPassword: {
ask: "¿Olvidaste tu contraseña?", ask: '¿Olvidaste tu contraseña?',
description: description: '¿Ha olvidado su contraseña? No hay problema. Sólo tienes que indicarnos tu dirección de correo electrónico y te enviaremos un enlace para restablecer la contraseña que te permitirá elegir una nueva.',
"¿Ha olvidado su contraseña? No hay problema. Sólo tienes que indicarnos tu dirección de correo electrónico y te enviaremos un enlace para restablecer la contraseña que te permitirá elegir una nueva.", sendLink: 'Enviar enlace de recuperación por correo',
sendLink: "Enviar enlace de recuperación por correo", title: 'Contraseña olvidada',
title: "Contraseña olvidada",
}, },
login: "Iniciar sesión", login: 'Iniciar sesión',
logout: "Cerrar sesión", logout: 'Cerrar sesión',
register: { register: {
already: "¿Ya estas registrado?", already: '¿Ya estas registrado?',
me: "Registrarme", me: 'Registrarme',
}, },
remember: "Recuerdame", remember: 'Recuerdame',
}, },
code: "Código", code:'Código',
cancel: "Cancelar", cancel:'Cancelar',
changes: "Cambios", changes:'Cambios',
changelogs: { changelogs: {
title: "Historial de cambios", title:'Historial de cambios',
description: "Lista de los cambios realizados al sistema.", description: 'Lista de los cambios realizados al sistema.',
}, },
close:"Cerrar", close:"Cerrar",
confirm: "Confirmar", confirm:'Confirmar',
copyright: "Todos los derechos reservados.", copyright:'Todos los derechos reservados.',
contact: "Contacto", contact:'Contacto',
crud: { crud: {
create: "Nuevo registro", create: 'Nuevo registro',
edit: "Editar registro", edit: 'Editar registro',
destroy: "Eliminar registro", destroy: 'Eliminar registro',
show: "Más detalles", show: 'Más detalles',
}, },
date: "Fecha", date: 'Fecha',
delete:{ delete:{
confirm: confirm: 'Al presionar ELIMINAR el registro se eliminará permanentemente y no podrá recuperarse.',
"Al presionar ELIMINAR el registro se eliminará permanentemente y no podrá recuperarse.", title: 'Eliminar',
title: "Eliminar",
}, },
deleted: "Eliminado", deleted:'Eliminado',
description: "Descripción", description:'Descripción',
details: "Detalles", details:'Detalles',
disable: "Deshabilitar", disable:'Deshabilitar',
disabled: "Deshabilitado", disabled:'Deshabilitado',
done: "Hecho.", done:'Hecho.',
edit: "Editar", edit:'Editar',
email:{ email:{
title: "Correo", title:'Correo',
verification: "Verificar correo", verification:'Verificar correo'
}, },
enable: "Habilitar", enable:'Habilitar',
endDate: "Fecha Fin", endDate:'Fecha Fin',
event: "Evento", event:'Evento',
help: { help: {
description: description:'A continuación se lista la iconografía para entender el funcionamiento del sistema.',
"A continuación se lista la iconografía para entender el funcionamiento del sistema.", home: 'Volver a la pagina de inicio.',
home: "Volver a la pagina de inicio.", title:'Ayuda',
title: "Ayuda",
}, },
history: { history: {
title: "Historial de acciones", title:'Historial de acciones',
description: description:'Historial de acciones realizadas por los usuarios en orden cronológico.'
"Historial de acciones realizadas por los usuarios en orden cronológico.",
}, },
home: "Inicio", home:'Inicio',
hour: "Hora", hour:'Hora',
icon: "Icono", icon:'Icono',
maternal: "Apellido materno", maternal:'Apellido materno',
menu: "Menú", menu:'Menú',
name: "Nombre", name:'Nombre',
noRecords: "Sin registros", noRecords:'Sin registros',
notifications: { notifications: {
readed: "Marcar como leído", readed:'Marcar como leído',
deleted: "Notificación eliminada", deleted:'Notificación eliminada',
description: "Notificaciones del usuario", description:'Notificaciones del usuario',
notFound: "Notificación no encontrada", notFound:'Notificación no encontrada',
title: "Notificaciones", title:'Notificaciones',
}, },
password: "Contraseña", password:'Contraseña',
passwordConfirmation: "Confirmar contraseña", passwordConfirmation:'Confirmar contraseña',
passwordCurrent: "Contraseña actual", passwordCurrent:'Contraseña actual',
passwordReset: "Restaurar contraseña", passwordReset:'Restaurar contraseña',
paternal: "Apellido paterno", paternal:'Apellido paterno',
phone: "Teléfono", phone:'Teléfono',
photo: { photo: {
new: "Seleccionar una nueva foto", new: 'Seleccionar una nueva foto',
remove: "Remover foto", remove:'Remover foto',
title: "Foto", title:'Foto',
}, },
profile: "Perfil", profile:'Perfil',
readed: "Leído", readed:'Leído',
register: { register: {
agree: "Estoy de acuerdo con los", agree:'Estoy de acuerdo con los',
privacy: "Política de Privacidad", privacy:'Política de Privacidad',
signUp: "Registrarme", signUp:'Registrarme',
terms: "Términos de Servicio", terms:'Términos de Servicio',
}, },
registers:{ registers:{
title: "Registros", title:'Registros',
empty: "Sin registros", empty:'Sin registros',
}, },
remove: "Remover", remove: 'Remover',
return: "Regresar", return: 'Regresar',
role: "Rol", role:'Rol',
roles:{ roles:{
create: { create: {
title: "Crear rol", title: 'Crear rol',
description: "Estos roles serán usados para dar permisos en el sistema.", description: 'Estos roles serán usados para dar permisos en el sistema.',
onSuccess: "Rol creado exitosamente", onSuccess: 'Rol creado exitosamente',
onError: "Error al crear el role", onError: 'Error al crear el role',
}, },
deleted: "Rol eliminado", deleted:'Rol eliminado',
title: "Roles", title: 'Roles',
}, },
save: "Guardar", save:'Guardar',
saved: "¡Guardado!", saved:'¡Guardado!',
search: "Buscar", search:'Buscar',
selected: "Seleccionado", selected: 'Seleccionado',
select: "Seleccionar", select: 'Seleccionar',
setting: "Configuración", setting: 'Configuración',
show: { show: {
all: "Mostrar todo", all:'Mostrar todo',
title: "Mostrar", title:'Mostrar',
}, },
startDate: "Fecha de inicio", startDate:'Fecha de inicio',
status: "Estado", status:'Estado',
terms: { terms: {
agree: "Estoy de acuerdo con los", agree:'Estoy de acuerdo con los',
privacy: "Política de privacidad", privacy:'Política de privacidad',
service: "Términos de servicio", service:'Términos de servicio',
}, },
unknown: "Desconocido", unknown:'Desconocido',
update: "Actualizar", update:'Actualizar',
updated: "Actualizado", updated:'Actualizado',
updateFail: "Error al actualizar", updateFail:'Error al actualizar',
unreaded: "No leído", unreaded:'No leído',
user: "Usuario", user:'Usuario',
users:{ users:{
create:{ create:{
title: "Crear usuario", title:'Crear usuario',
description: description:'Permite crear nuevos usuarios. No olvides otorgarle roles para que pueda acceder a las partes del sistema deseados.',
"Permite crear nuevos usuarios. No olvides otorgarle roles para que pueda acceder a las partes del sistema deseados.", onSuccess:'Usuario creado',
onSuccess: "Usuario creado", onError:'Ocurrió un error al crear el usuario'
onError: "Ocurrió un error al crear el usuario",
}, },
deleted: "Usuario eliminado", deleted:'Usuario eliminado',
notFount: "Usuario no encontrado", notFount:'Usuario no encontrado',
password: { password: {
description: description:'Permite actualizar las contraseñas de los usuarios sobreescribiendola.',
"Permite actualizar las contraseñas de los usuarios sobreescribiendola.", title:'Actualizar contraseña',
title: "Actualizar contraseña",
}, },
roles: { roles: {
description: description:'Actualiza los roles de los usuarios, permitiendo o denegando los accesos a determinadas áreas.',
"Actualiza los roles de los usuarios, permitiendo o denegando los accesos a determinadas áreas.",
error:{ error:{
min: "Seleccionar mínimo un role", min:'Seleccionar mínimo un role'
}, },
title: "Roles de usuario", title:'Roles de usuario',
}, },
menu: "Menú de usuario", menu:'Menú de usuario',
select: "Seleccionar un usuario", select:'Seleccionar un usuario',
settings: "Ajustes del usuario", settings:'Ajustes del usuario',
system: "Usuarios del sistema", system:'Usuarios del sistema',
title: "Usuarios", title:'Usuarios',
}, },
version:'Versión',
scores: {
system: "Sistema de puntuación",
title: "Puntuaciones",
create: {
title: "Crear puntuación",
description: "Permite crear nuevas puntuaciones para los usuarios.",
onSuccess: "Puntuación creada exitosamente",
onError: "Error al crear la puntuación",
},
},
department: {
system: "Sistema de departamentos",
title: "Departamentos",
create: {
title: "Crear departamento",
description: "Permite crear nuevos departamentos para los usuarios.",
onSuccess: "Departamento creado exitosamente",
onError: "Error al crear el departamento",
},
select:{
title: "Seleccionar un departamento",
description: "Seleccione un departamento para filtrar los roles.",
} }
},
mainRole: {
title: "Roles principales",
inDepartment: "Roles principales en el departamento",
system: "Sistema de roles principales",
select:{
description: "Seleccione un rol principal.",
},
create: {
title: "Crear rol principal",
description: "Permite crear nuevos roles principales para los usuarios.",
onSuccess: "Rol principal creado exitosamente",
onError: "Error al crear el rol principal",
},
},
skill: {
title: "Habilidades",
assignTo: "Asignar a rol principal",
system: "Sistema de habilidades",
assignment: {
description: "Asigna una habilidad a un rol principal.",
},
create: {
title: "Crear habilidad",
description:
"Permite crear nuevas habilidades para los roles principales.",
onSuccess: "Habilidad creada exitosamente",
onError: "Error al crear la habilidad",
},
deleted: "Habilidad eliminada",
},
mainRoleSkills: {
system: "Sistema de habilidades de rol principal",
create: {
title: "Crear habilidad de rol principal",
description:
"Permite crear nuevas habilidades para los roles principales.",
onSuccess: "Habilidad de rol principal creada exitosamente",
onError: "Error al crear la habilidad de rol principal",
},
},
version: "Versión",
};

View File

@ -75,31 +75,6 @@ onMounted(()=> {
name="users.title" name="users.title"
to="admin.users.index" to="admin.users.index"
/> />
<Link
icon="business_center"
name="Departamentos"
to="admin.departments.index"
/>
<Link
icon="assignment_ind"
name="Rol Principal"
to="admin.mainRoles.index"
/>
<Link
icon="psychology"
name="Habilidades"
to="admin.skills.index"
/>
<Link
icon="leaderboard"
name="Scores"
to="admin.scores.index"
/>
<Link
icon="sports_handball"
name="Roles Puntuados"
to="admin.mainRoleSkills.index"
/>
<Link <Link
icon="history" icon="history"
name="changelogs.title" name="changelogs.title"

View File

@ -1,15 +0,0 @@
import { t } from '@/Lang/i18n';
import { hasPermission } from '@/rolePermission.js';
// Obtener ruta
const goTo = (route) => `admin.scores.${route}`
// Obtener traducción del componente
const transl = (lang) => t(`scores.${lang}`)
// Determina si un usuario puede hacer algo no en base a los permisos
const can = (permission) => hasPermission(`users.${permission}`)
export {
can,
goTo,
transl
}

View File

@ -1,84 +0,0 @@
<script setup>
import { goTo, transl } from './Component';
import { Link, useForm } from '@inertiajs/vue3';
import PrimaryButton from '@/Components/Dashboard/Button/Primary.vue';
import Input from '@/Components/Dashboard/Form/Input.vue';
import PageHeader from '@/Components/Dashboard/PageHeader.vue';
import GoogleIcon from '@/Components/Shared/GoogleIcon.vue';
import DashboardLayout from '@/Layouts/DashboardLayout.vue';
defineProps({
alias: String,
value: Number,
description: String
});
const form = useForm({
alias: '',
value: '',
description: '',
});
const submit = () => form.post(route(goTo('store')), {
onSuccess: () => Notify.success(transl('create.onSuccess')),
onError: () => Notify.error(transl('create.onError')),
onFinish: () => form.reset('password')
});
</script>
<template>
<DashboardLayout :title="transl('create.title')">
<PageHeader>
<Link :href="route(goTo('index'))">
<GoogleIcon
:title="$t('return')"
class="btn-icon-primary"
name="arrow_back"
outline
/>
</Link>
</PageHeader>
<div class="w-full pb-8">
<div class="mt-8">
<p
v-text="transl('create.description')"
/>
</div>
</div>
<div class="w-full">
<form @submit.prevent="submit" class="grid gap-4 grid-cols-6">
<Input
id="Alias"
class="col-span-2"
v-model="form.alias"
:onError="form.errors.alias"
autofocus
required
/>
<Input
id="Valor"
class="col-span-2"
v-model="form.value"
:onError="form.errors.value"
autofocus
required
/>
<Input
id="Descrición"
class="col-span-2"
v-model="form.description"
:onError="form.errors.description"
autofocus
/>
<div class="col-span-6 flex flex-col items-center justify-end space-y-4 mt-4">
<PrimaryButton
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
v-text="transl('create.title')"
/>
</div>
</form>
</div>
</DashboardLayout>
</template>

View File

@ -1,43 +0,0 @@
<script setup>
import { transl, goTo } from './Component'
import { router } from '@inertiajs/vue3';
import DestroyModal from '@/Components/Dashboard/Modal/Destroy.vue';
import Header from '@/Components/Dashboard/Modal/Elements/Header.vue';
const emit = defineEmits([
'close',
'switchModal'
]);
const props = defineProps({
show: Boolean,
model: Object
});
const destroy = (id) => router.delete(route(goTo('destroy'), {id}), {
preserveScroll: true,
onSuccess: () => {
props.model.pop;
Notify.success(transl('deleted'));
emit('close');
},
onError: () => {
Notify.info(transl('notFound'));
emit('close');
}
});
</script>
<template>
<DestroyModal
:show="show"
@close="$emit('close')"
@destroy="destroy(model.id)"
>
<Header
:title="model.alias"
:subtitle="model.value"
/>
</DestroyModal>
</template>

View File

@ -1,64 +0,0 @@
<script setup>
import { goTo } from "./Component";
import { useForm } from "@inertiajs/vue3";
import { onUpdated } from "vue";
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";
const emit = defineEmits(["close", "switchModal"]);
const props = defineProps({
show: Boolean,
model: Object,
});
const form = useForm({});
const update = (id) => {
form
.transform((data) => ({
...props.model,
}))
.put(route(goTo("update"), { id }), {
preserveScroll: true,
onSuccess: () => {
Notify.success(lang("updated"));
emit("switchModal");
},
});
};
</script>
<template>
<EditModal :show="show" @close="$emit('close')" @update="update(model.id)">
<Header :title="model.alias" />
<div class="py-2 border-b">
<div class="p-4">
<form>
<div class="grid gap-6 mb-6 lg:grid-cols-2">
<Input
id="Alias"
placeholder="Alias"
v-model="model.alias"
:onError="form.errors.alias"
required
/>
<Input
id="Valor"
v-model="model.value"
:onError="form.errors.value"
required
/>
<Input
id="Descripción"
v-model="model.description"
:onError="form.errors.description"
required
/>
</div>
</form>
</div>
</div>
</EditModal>
</template>

View File

@ -1,158 +0,0 @@
<script setup>
import { transl, can, goTo } from './Component'
import { ref } 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 ShowView from './Show.vue';
const props = defineProps({
scores: Object
});
// Controladores
const Modal = new ModalController();
const Searcher = new SearcherController(goTo('index'));
// Variables de controladores
const destroyModal = ref(Modal.destroyModal);
const editModal = ref(Modal.editModal);
const showModal = ref(Modal.showModal);
const modelModal = ref(Modal.modelModal);
const query = ref(Searcher.query);
</script>
<template>
<DashboardLayout :title="transl('system')">
<SearcherHead @search="Searcher.search">
<Link
v-if="can('create')"
:href="route(goTo('create'))"
>
<GoogleIcon
:title="$t('crud.create')"
class="btn-icon-primary"
name="add"
outline
/>
</Link>
</SearcherHead>
<div class="pt-2 w-full">
<Table
:items="scores"
@send-pagination="Searcher.searchWithPagination"
>
<template #head>
<th
class="table-item"
v-text="$t('Alias')"
/>
<th
class="table-item"
v-text="$t('Valor')"
/>
<th
class="table-item"
v-text="$t('Descripción')"
/>
<th
class="table-item w-44"
v-text="$t('actions')"
/>
</template>
<template #body="{items}">
<tr v-for="model in items">
<td class="table-item border">
<div class="flex items-center text-sm">
<div>
<p class="font-semibold">
{{ model.alias }}
</p>
</div>
</div>
</td>
<td class="table-item border">
<div class="flex items-center text-sm">
<div>
<p class="font-semibold">
{{ model.value }}
</p>
</div>
</div>
</td>
<td class="table-item border">
<div class="flex items-center text-sm">
<div>
<p class="font-semibold">
{{ model.description }}
</p>
</div>
</div>
</td>
<td class="table-item border">
<div class="flex justify-center space-x-2">
<GoogleIcon
:title="$t('crud.show')"
class="btn-icon-primary"
name="visibility"
outline
@click="Modal.switchShowModal(model)"
/>
<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')"
:show="destroyModal"
:model="modelModal"
@close="Modal.switchDestroyModal"
/>
</DashboardLayout>
</template>

View File

@ -1,41 +0,0 @@
<script setup>
import { can } from './Component'
import Header from '@/Components/Dashboard/Modal/Elements/Header.vue';
import ShowModal from '@/Components/Dashboard/Modal/Show.vue';
import GoogleIcon from '@/Components/Shared/GoogleIcon.vue';
defineEmits([
'close',
'switchModal'
]);
defineProps({
show: Boolean,
model: Object
});
</script>
<template>
<ShowModal
:show="show"
:editable="can('edit')"
@close="$emit('close')"
@edit="$emit('switchModal')"
editable
>
<Header
:title="model.alias"
>
</Header>
<div class="py-2 border-b">
<div class="px-4 py-2 flex">
<GoogleIcon
class="text-xl text-success"
name="contact_mail"
/>
<div class="pl-3">
</div>
</div>
</div>
</ShowModal>
</template>

View File

@ -1,15 +0,0 @@
import { t } from '@/Lang/i18n';
import { hasPermission } from '@/rolePermission.js';
// Obtener ruta
const goTo = (route) => `admin.departments.${route}`
// Obtener traducción del componente
const transl = (lang) => t(`department.${lang}`)
// Determina si un usuario puede hacer algo no en base a los permisos
const can = (permission) => hasPermission(`users.${permission}`)
export {
can,
goTo,
transl
}

View File

@ -1,73 +0,0 @@
<script setup>
import { goTo, transl } from './Component';
import { Link, useForm } from '@inertiajs/vue3';
import PrimaryButton from '@/Components/Dashboard/Button/Primary.vue';
import Input from '@/Components/Dashboard/Form/Input.vue';
import PageHeader from '@/Components/Dashboard/PageHeader.vue';
import GoogleIcon from '@/Components/Shared/GoogleIcon.vue';
import DashboardLayout from '@/Layouts/DashboardLayout.vue';
defineProps({
name: String,
description: String
});
const form = useForm({
name: '',
description: '',
});
const submit = () => form.post(route(goTo('store')), {
onSuccess: () => Notify.success(transl('create.onSuccess')),
onError: () => Notify.error(transl('create.onError')),
});
</script>
<template>
<DashboardLayout :title="transl('create.title')">
<PageHeader>
<Link :href="route(goTo('index'))">
<GoogleIcon
:title="$t('return')"
class="btn-icon-primary"
name="arrow_back"
outline
/>
</Link>
</PageHeader>
<div class="w-full pb-8">
<div class="mt-8">
<p
v-text="transl('create.description')"
/>
</div>
</div>
<div class="w-full">
<form @submit.prevent="submit" class="grid gap-4 grid-cols-6">
<Input
id="Nombre"
class="col-span-2"
v-model="form.name"
:onError="form.errors.name"
autofocus
required
/>
<Input
id="Descrición"
class="col-span-2"
v-model="form.description"
:onError="form.errors.description"
autofocus
/>
<div class="col-span-6 flex flex-col items-center justify-end space-y-4 mt-4">
<PrimaryButton
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
v-text="transl('create.title')"
/>
</div>
</form>
</div>
</DashboardLayout>
</template>

View File

@ -1,43 +0,0 @@
<script setup>
import { transl, goTo } from './Component'
import { router } from '@inertiajs/vue3';
import DestroyModal from '@/Components/Dashboard/Modal/Destroy.vue';
import Header from '@/Components/Dashboard/Modal/Elements/Header.vue';
const emit = defineEmits([
'close',
'switchModal'
]);
const props = defineProps({
show: Boolean,
model: Object
});
const destroy = (id) => router.delete(route(goTo('destroy'), {id}), {
preserveScroll: true,
onSuccess: () => {
props.model.pop;
Notify.success(transl('deleted'));
emit('close');
},
onError: () => {
Notify.info(transl('notFound'));
emit('close');
}
});
</script>
<template>
<DestroyModal
:show="show"
@close="$emit('close')"
@destroy="destroy(model.id)"
>
<Header
:title="model.name"
:subtitle="model.description"
/>
</DestroyModal>
</template>

View File

@ -1,67 +0,0 @@
<script setup>
import { goTo } from './Component';
import { useForm } from '@inertiajs/vue3';
import { onUpdated } from 'vue';
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';
const emit = defineEmits([
'close',
'switchModal'
]);
const props = defineProps({
show: Boolean,
model: Object
});
const form = useForm({});
const update = (id) => {
form.transform(data => ({
...props.model
})).put(route(goTo('update'), {id}),{
preserveScroll: true,
onSuccess: () => {
Notify.success(lang('updated'))
emit('switchModal')
}
});
}
</script>
<template>
<EditModal
:show="show"
@close="$emit('close')"
@update="update(model.id)"
>
<Header
:title="model.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"
required
/>
</div>
</form>
</div>
</div>
</EditModal>
</template>

View File

@ -1,137 +0,0 @@
<script setup>
import { transl, can, goTo } from './Component'
import { ref } 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 ShowView from './Show.vue';
const props = defineProps({
departments: Object
});
// Controladores
const Modal = new ModalController();
const Searcher = new SearcherController(goTo('index'));
// Variables de controladores
const destroyModal = ref(Modal.destroyModal);
const editModal = ref(Modal.editModal);
const showModal = ref(Modal.showModal);
const modelModal = ref(Modal.modelModal);
const query = ref(Searcher.query);
</script>
<template>
<DashboardLayout :title="transl('system')">
<SearcherHead @search="Searcher.search">
<Link
v-if="can('create')"
:href="route(goTo('create'))"
>
<GoogleIcon
:title="$t('crud.create')"
class="btn-icon-primary"
name="add"
outline
/>
</Link>
</SearcherHead>
<div class="pt-2 w-full">
<Table
:items="departments"
@send-pagination="Searcher.searchWithPagination"
>
<template #head>
<th
class="table-item"
v-text="$t('Nombre')"
/>
<th
class="table-item"
v-text="$t('Descripción')"
/>
<th
class="table-item w-44"
v-text="$t('actions')"
/>
</template>
<template #body="{items}">
<tr v-for="model in items">
<td class="table-item border">
<div class="flex items-center text-sm">
<div>
<p class="font-semibold">
{{ model.name }}
</p>
</div>
</div>
</td>
<td class="table-item border">
<div class="flex items-center text-sm">
<div>
<p class="font-semibold">
{{ model.description }}
</p>
</div>
</div>
</td>
<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')"
:show="destroyModal"
:model="modelModal"
@close="Modal.switchDestroyModal"
/>
</DashboardLayout>
</template>

View File

@ -1,41 +0,0 @@
<script setup>
import { can } from './Component'
import Header from '@/Components/Dashboard/Modal/Elements/Header.vue';
import ShowModal from '@/Components/Dashboard/Modal/Show.vue';
import GoogleIcon from '@/Components/Shared/GoogleIcon.vue';
defineEmits([
'close',
'switchModal'
]);
defineProps({
show: Boolean,
model: Object
});
</script>
<template>
<ShowModal
:show="show"
:editable="can('edit')"
@close="$emit('close')"
@edit="$emit('switchModal')"
editable
>
<Header
:title="model.alias"
>
</Header>
<div class="py-2 border-b">
<div class="px-4 py-2 flex">
<GoogleIcon
class="text-xl text-success"
name="contact_mail"
/>
<div class="pl-3">
</div>
</div>
</div>
</ShowModal>
</template>

View File

@ -1,15 +0,0 @@
import { t } from '@/Lang/i18n';
import { hasPermission } from '@/rolePermission.js';
// Obtener ruta
const goTo = (route) => `admin.mainRoles.${route}`
// Obtener traducción del componente
const transl = (lang) => t(`mainRole.${lang}`)
// Determina si un usuario puede hacer algo no en base a los permisos
const can = (permission) => hasPermission(`users.${permission}`)
export {
can,
goTo,
transl
}

View File

@ -1,89 +0,0 @@
<script setup>
import { goTo, transl } from "./Component";
import { Link, useForm } from "@inertiajs/vue3";
import PrimaryButton from "@/Components/Dashboard/Button/Primary.vue";
import Input from "@/Components/Dashboard/Form/Input.vue";
import PageHeader from "@/Components/Dashboard/PageHeader.vue";
import GoogleIcon from "@/Components/Shared/GoogleIcon.vue";
import DashboardLayout from "@/Layouts/DashboardLayout.vue";
import Selectable from "@/Components/Dashboard/Form/Selectable.vue";
defineProps({
departments: Object,
});
const form = useForm({
name: "",
description: "",
department_id: "",
});
const submit = () =>
form
.transform((data) => ({
...data,
department_id: form.department_id?.id,
}))
.post(route(goTo("store")), {
onSuccess: () => Notify.success(transl("create.onSuccess")),
onError: () => Notify.error(transl("create.onError")),
});
</script>
<template>
<DashboardLayout :title="transl('create.title')">
<PageHeader>
<Link :href="route(goTo('index'))">
<GoogleIcon
:title="$t('return')"
class="btn-icon-primary"
name="arrow_back"
outline
/>
</Link>
</PageHeader>
<div class="w-full pb-8">
<div class="mt-8">
<p v-text="transl('create.description')" />
</div>
</div>
<div class="w-full">
<form @submit.prevent="submit" class="grid gap-4 grid-cols-6">
<Input
id="Nombre"
class="col-span-2"
v-model="form.name"
:onError="form.errors.name"
autofocus
required
/>
<Input
id="Descrición"
class="col-span-2"
v-model="form.description"
:onError="form.errors.description"
autofocus
/>
<Selectable
id="department_id"
class="col-span-3"
v-model="form.department_id"
:options="departments"
:onError="form.errors.department_id"
:title="$t('department.title')"
required
/>
<div
class="col-span-6 flex flex-col items-center justify-end space-y-4 mt-4"
>
<PrimaryButton
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
v-text="transl('create.title')"
/>
</div>
</form>
</div>
</DashboardLayout>
</template>

View File

@ -1,43 +0,0 @@
<script setup>
import { transl, goTo } from './Component'
import { router } from '@inertiajs/vue3';
import DestroyModal from '@/Components/Dashboard/Modal/Destroy.vue';
import Header from '@/Components/Dashboard/Modal/Elements/Header.vue';
const emit = defineEmits([
'close',
'switchModal'
]);
const props = defineProps({
show: Boolean,
model: Object
});
const destroy = (id) => router.delete(route(goTo('destroy'), {id}), {
preserveScroll: true,
onSuccess: () => {
props.model.pop;
Notify.success(transl('deleted'));
emit('close');
},
onError: () => {
Notify.info(transl('notFound'));
emit('close');
}
});
</script>
<template>
<DestroyModal
:show="show"
@close="$emit('close')"
@destroy="destroy(model.id)"
>
<Header
:title="model.name"
:subtitle="model.description"
/>
</DestroyModal>
</template>

View File

@ -1,67 +0,0 @@
<script setup>
import { goTo } from './Component';
import { useForm } from '@inertiajs/vue3';
import { onUpdated } from 'vue';
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';
const emit = defineEmits([
'close',
'switchModal'
]);
const props = defineProps({
show: Boolean,
model: Object
});
const form = useForm({});
const update = (id) => {
form.transform(data => ({
...props.model
})).put(route(goTo('update'), {id}),{
preserveScroll: true,
onSuccess: () => {
Notify.success(lang('updated'))
emit('switchModal')
}
});
}
</script>
<template>
<EditModal
:show="show"
@close="$emit('close')"
@update="update(model.id)"
>
<Header
:title="model.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"
required
/>
</div>
</form>
</div>
</div>
</EditModal>
</template>

View File

@ -1,151 +0,0 @@
<script setup>
import { transl, can, goTo } from './Component'
import { ref } 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 ShowView from './Show.vue';
const props = defineProps({
mainRoles: Object
});
// Controladores
const Modal = new ModalController();
const Searcher = new SearcherController(goTo('index'));
// Variables de controladores
const destroyModal = ref(Modal.destroyModal);
const editModal = ref(Modal.editModal);
const showModal = ref(Modal.showModal);
const modelModal = ref(Modal.modelModal);
const query = ref(Searcher.query);
</script>
<template>
<DashboardLayout :title="transl('system')">
<SearcherHead @search="Searcher.search">
<Link
v-if="can('create')"
:href="route(goTo('create'))"
>
<GoogleIcon
:title="$t('crud.create')"
class="btn-icon-primary"
name="add"
outline
/>
</Link>
</SearcherHead>
<div class="pt-2 w-full">
<Table
:items="mainRoles"
@send-pagination="Searcher.searchWithPagination"
>
<template #head>
<th
class="table-item"
v-text="$t('Nombre')"
/>
<th
class="table-item"
v-text="$t('Descripción')"
/>
<th
class="table-item"
v-text="$t('Departamento')"
/>
<th
class="table-item w-44"
v-text="$t('actions')"
/>
</template>
<template #body="{items}">
<tr v-for="model in items">
<td class="table-item border">
<div class="flex items-center text-sm">
<div>
<p class="font-semibold">
{{ model.name }}
</p>
</div>
</div>
</td>
<td class="table-item border">
<div class="flex items-center text-sm">
<div>
<p class="font-semibold">
{{ model.description }}
</p>
</div>
</div>
</td>
<td class="table-item border">
<div class="flex items-center text-sm">
<div>
<p class="font-semibold">
{{ model.department?.name }}
</p>
</div>
</div>
</td>
<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')"
:show="destroyModal"
:model="modelModal"
@close="Modal.switchDestroyModal"
/>
</DashboardLayout>
</template>

View File

@ -1,41 +0,0 @@
<script setup>
import { can } from './Component'
import Header from '@/Components/Dashboard/Modal/Elements/Header.vue';
import ShowModal from '@/Components/Dashboard/Modal/Show.vue';
import GoogleIcon from '@/Components/Shared/GoogleIcon.vue';
defineEmits([
'close',
'switchModal'
]);
defineProps({
show: Boolean,
model: Object
});
</script>
<template>
<ShowModal
:show="show"
:editable="can('edit')"
@close="$emit('close')"
@edit="$emit('switchModal')"
editable
>
<Header
:title="model.alias"
>
</Header>
<div class="py-2 border-b">
<div class="px-4 py-2 flex">
<GoogleIcon
class="text-xl text-success"
name="contact_mail"
/>
<div class="pl-3">
</div>
</div>
</div>
</ShowModal>
</template>

View File

@ -1,15 +0,0 @@
import { t } from '@/Lang/i18n';
import { hasPermission } from '@/rolePermission.js';
// Obtener ruta
const goTo = (route) => `admin.mainRoleSkills.${route}`
// Obtener traducción del componente
const transl = (lang) => t(`mainRoleSkills.${lang}`)
// Determina si un usuario puede hacer algo no en base a los permisos
const can = (permission) => hasPermission(`users.${permission}`)
export{
can,
goTo,
transl
}

View File

@ -1,163 +0,0 @@
<script setup>
import { goTo, transl } from "./Component";
import { Link, useForm } from "@inertiajs/vue3";
import { ref, computed } 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,
});
// Estado del flujo
const currentStep = ref(1);
const selectedDepartment = ref(null);
const selectedMainRole = ref(null);
const form = useForm({
main_role_id: "",
skill_id: "",
scored_id: "",
});
// Datos computados
const uniqueDepartments = computed(() => {
const deps = [];
const seen = new Set();
props.mainRoles.forEach((role) => {
if (role.department && !seen.has(role.department.id)) {
seen.add(role.department.id);
deps.push(role.department);
}
});
return deps;
});
// Métodos de navegación
const handleDepartmentSelect = (department) => {
selectedDepartment.value = department;
currentStep.value = 2;
};
const handleMainRoleSelect = (mainRole) => {
selectedMainRole.value = mainRole;
form.main_role_id = mainRole.id;
currentStep.value = 3;
};
const goBack = () => {
if (currentStep.value === 3) {
selectedMainRole.value = null;
form.main_role_id = "";
form.skill_id = "";
form.scored_id = "";
currentStep.value = 2;
} else if (currentStep.value === 2) {
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")),
});
};
</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>
</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>
</div>
<!-- Componentes de pasos -->
<DepartmentSelector
v-if="currentStep === 1"
:departments="uniqueDepartments"
@select="handleDepartmentSelect"
/>
<MainRoleSelector
v-if="currentStep === 2"
:main-roles="mainRoles"
:selected-department="selectedDepartment"
@select="handleMainRoleSelect"
/>
<SkillAssignment
v-if="currentStep === 3"
:form="form"
:skills="skills"
:scores="scores"
:selected-department="selectedDepartment"
:selected-main-role="selectedMainRole"
@submit="submit"
/>
</DashboardLayout>
</template>

View File

@ -1,43 +0,0 @@
<script setup>
import { transl, goTo } from './Component'
import { router } from '@inertiajs/vue3';
import DestroyModal from '@/Components/Dashboard/Modal/Destroy.vue';
import Header from '@/Components/Dashboard/Modal/Elements/Header.vue';
const emit = defineEmits([
'close',
'switchModal'
]);
const props = defineProps({
show: Boolean,
model: Object
});
const destroy = (id) => router.delete(route(goTo('destroy'), {id}), {
preserveScroll: true,
onSuccess: () => {
props.model.pop;
Notify.success(transl('deleted'));
emit('close');
},
onError: () => {
Notify.info(transl('notFound'));
emit('close');
}
});
</script>
<template>
<DestroyModal
:show="show"
@close="$emit('close')"
@destroy="destroy(model.id)"
>
<Header
:title="model.name"
:subtitle="model.description"
/>
</DestroyModal>
</template>

View File

@ -1,67 +0,0 @@
<script setup>
import { goTo } from './Component';
import { useForm } from '@inertiajs/vue3';
import { onUpdated } from 'vue';
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';
const emit = defineEmits([
'close',
'switchModal'
]);
const props = defineProps({
show: Boolean,
model: Object
});
const form = useForm({});
const update = (id) => {
form.transform(data => ({
...props.model
})).put(route(goTo('update'), {id}),{
preserveScroll: true,
onSuccess: () => {
Notify.success(lang('updated'))
emit('switchModal')
}
});
}
</script>
<template>
<EditModal
:show="show"
@close="$emit('close')"
@update="update(model.id)"
>
<Header
:title="model.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"
required
/>
</div>
</form>
</div>
</div>
</EditModal>
</template>

View File

@ -1,146 +0,0 @@
<script setup>
import { transl, can, goTo } 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";
const props = defineProps({
mainRoleSkills: Object,
});
// Controladores
const Modal = new ModalController();
const Searcher = new SearcherController(goTo("index"));
// Variables de controladores
const destroyModal = ref(Modal.destroyModal);
const editModal = ref(Modal.editModal);
const modelModal = ref(Modal.modelModal);
const query = ref(Searcher.query);
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);
});
</script>
<template>
<DashboardLayout :title="transl('system')">
<SearcherHead @search="Searcher.search">
<Link v-if="can('create')" :href="route(goTo('create'))">
<GoogleIcon
:title="$t('crud.create')"
class="btn-icon-primary"
name="add"
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"
/>
<DestroyView
v-if="can('create')"
:show="destroyModal"
:model="modelModal"
@close="Modal.switchDestroyModal"
/>
</DashboardLayout>
</template>

View File

@ -1,15 +0,0 @@
import { t } from '@/Lang/i18n';
import { hasPermission } from '@/rolePermission.js';
// Obtener ruta
const goTo = (route) => `admin.skills.${route}`
// Obtener traducción del componente
const transl = (lang) => t(`skill.${lang}`)
// Determina si un usuario puede hacer algo no en base a los permisos
const can = (permission) => hasPermission(`users.${permission}`)
export{
can,
goTo,
transl
}

View File

@ -1,89 +0,0 @@
<script setup>
import { goTo, transl } from "./Component";
import { Link, useForm } from "@inertiajs/vue3";
import PrimaryButton from "@/Components/Dashboard/Button/Primary.vue";
import Input from "@/Components/Dashboard/Form/Input.vue";
import PageHeader from "@/Components/Dashboard/PageHeader.vue";
import GoogleIcon from "@/Components/Shared/GoogleIcon.vue";
import DashboardLayout from "@/Layouts/DashboardLayout.vue";
import Selectable from "@/Components/Dashboard/Form/Selectable.vue";
defineProps({
departments: Object,
});
const form = useForm({
name: "",
description: "",
department_id: "",
});
const submit = () =>
form
.transform((data) => ({
...data,
department_id: form.department_id?.id,
}))
.post(route(goTo("store")), {
onSuccess: () => Notify.success(transl("create.onSuccess")),
onError: () => Notify.error(transl("create.onError")),
});
</script>
<template>
<DashboardLayout :title="transl('create.title')">
<PageHeader>
<Link :href="route(goTo('index'))">
<GoogleIcon
:title="$t('return')"
class="btn-icon-primary"
name="arrow_back"
outline
/>
</Link>
</PageHeader>
<div class="w-full pb-8">
<div class="mt-8">
<p v-text="transl('create.description')" />
</div>
</div>
<div class="w-full">
<form @submit.prevent="submit" class="grid gap-4 grid-cols-6">
<Input
id="Nombre"
class="col-span-2"
v-model="form.name"
:onError="form.errors.name"
autofocus
required
/>
<Input
id="Descrición"
class="col-span-2"
v-model="form.description"
:onError="form.errors.description"
autofocus
/>
<Selectable
id="department_id"
class="col-span-3"
v-model="form.department_id"
:options="departments"
:onError="form.errors.department_id"
:title="$t('department.title')"
required
/>
<div
class="col-span-6 flex flex-col items-center justify-end space-y-4 mt-4"
>
<PrimaryButton
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
v-text="transl('create.title')"
/>
</div>
</form>
</div>
</DashboardLayout>
</template>

View File

@ -1,43 +0,0 @@
<script setup>
import { transl, goTo } from './Component'
import { router } from '@inertiajs/vue3';
import DestroyModal from '@/Components/Dashboard/Modal/Destroy.vue';
import Header from '@/Components/Dashboard/Modal/Elements/Header.vue';
const emit = defineEmits([
'close',
'switchModal'
]);
const props = defineProps({
show: Boolean,
model: Object
});
const destroy = (id) => router.delete(route(goTo('destroy'), {id}), {
preserveScroll: true,
onSuccess: () => {
props.model.pop;
Notify.success(transl('deleted'));
emit('close');
},
onError: () => {
Notify.info(transl('notFound'));
emit('close');
}
});
</script>
<template>
<DestroyModal
:show="show"
@close="$emit('close')"
@destroy="destroy(model.id)"
>
<Header
:title="model.name"
:subtitle="model.description"
/>
</DestroyModal>
</template>

View File

@ -1,67 +0,0 @@
<script setup>
import { goTo } from './Component';
import { useForm } from '@inertiajs/vue3';
import { onUpdated } from 'vue';
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';
const emit = defineEmits([
'close',
'switchModal'
]);
const props = defineProps({
show: Boolean,
model: Object
});
const form = useForm({});
const update = (id) => {
form.transform(data => ({
...props.model
})).put(route(goTo('update'), {id}),{
preserveScroll: true,
onSuccess: () => {
Notify.success(lang('updated'))
emit('switchModal')
}
});
}
</script>
<template>
<EditModal
:show="show"
@close="$emit('close')"
@update="update(model.id)"
>
<Header
:title="model.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"
required
/>
</div>
</form>
</div>
</div>
</EditModal>
</template>

View File

@ -1,149 +0,0 @@
<script setup>
import { transl, can, goTo } from './Component'
import { ref } 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';
const props = defineProps({
skills: Object
});
// Controladores
const Modal = new ModalController();
const Searcher = new SearcherController(goTo('index'));
// Variables de controladores
const destroyModal = ref(Modal.destroyModal);
const editModal = ref(Modal.editModal);
const modelModal = ref(Modal.modelModal);
const query = ref(Searcher.query);
</script>
<template>
<DashboardLayout :title="transl('system')">
<SearcherHead @search="Searcher.search">
<Link
v-if="can('create')"
:href="route(goTo('create'))"
>
<GoogleIcon
:title="$t('crud.create')"
class="btn-icon-primary"
name="add"
outline
/>
</Link>
</SearcherHead>
<div class="pt-2 w-full">
<Table
:items="skills"
@send-pagination="Searcher.searchWithPagination"
>
<template #head>
<th
class="table-item"
v-text="$t('Nombre')"
/>
<th
class="table-item"
v-text="$t('Descripción')"
/>
<th
class="table-item"
v-text="$t('Departamento')"
/>
<th
class="table-item w-44"
v-text="$t('actions')"
/>
</template>
<template #body="{items}">
<tr v-for="model in items">
<td class="table-item border">
<div class="flex items-center text-sm">
<div>
<p class="font-semibold">
{{ model.name }}
</p>
</div>
</div>
</td>
<td class="table-item border">
<div class="flex items-center text-sm">
<div>
<p class="font-semibold">
{{ model.description }}
</p>
</div>
</div>
</td>
<td class="table-item border">
<div class="flex items-center text-sm">
<div>
<p class="font-semibold">
{{ model.department?.name }}
</p>
</div>
</div>
</td>
<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')"
:show="destroyModal"
:model="modelModal"
@close="Modal.switchDestroyModal"
/>
</DashboardLayout>
</template>

View File

@ -1,17 +1,11 @@
<?php <?php
use App\Http\Controllers\Admin\ScoreController;
use App\Http\Controllers\Admin\UserController; use App\Http\Controllers\Admin\UserController;
use App\Http\Controllers\Dashboard\HistoryLogController; use App\Http\Controllers\Dashboard\HistoryLogController;
use App\Http\Controllers\Dashboard\IndexController; use App\Http\Controllers\Dashboard\IndexController;
use App\Http\Controllers\Dashboard\NotificationController; use App\Http\Controllers\Dashboard\NotificationController;
use App\Http\Controllers\Admin\DepartmentController;
use App\Http\Controllers\Admin\MainRoleController;
use App\Http\Controllers\Admin\MainRoleSkillsController;
use App\Http\Controllers\Admin\SkillController;
use App\Http\Controllers\Developer\RoleController; use App\Http\Controllers\Developer\RoleController;
use App\Http\Controllers\Example\IndexController as ExampleIndexController; use App\Http\Controllers\Example\IndexController as ExampleIndexController;
use App\Models\MainRoleSkills;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
/** /**
@ -20,6 +14,7 @@
* Rutas accesibles por todos los usuarios y no usuarios * Rutas accesibles por todos los usuarios y no usuarios
*/ */
Route::redirect('/', '/login'); Route::redirect('/', '/login');
/** /**
* Rutas del Dashboard * Rutas del Dashboard
* *
@ -58,11 +53,6 @@
config('jetstream.auth_session') config('jetstream.auth_session')
])->group(function () { ])->group(function () {
Route::resource('users', UserController::class); Route::resource('users', UserController::class);
Route::resource('scores', ScoreController::class);
Route::resource('departments', DepartmentController::class);
Route::resource('mainRoles', MainRoleController::class);
Route::resource('skills', SkillController::class);
Route::resource('mainRoleSkills', MainRoleSkillsController::class);
Route::prefix('/users')->name('users.')->group(function() Route::prefix('/users')->name('users.')->group(function()
{ {