diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php new file mode 100644 index 0000000000000000000000000000000000000000..b6598fdcf8e80576a7cb9aba5d81f5cef140139c --- /dev/null +++ b/app/Http/Controllers/UserController.php @@ -0,0 +1,108 @@ +get(); + return view('adminGen.usuarios.index', ['usuarios' => $usuarios]); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + $dependencias = Dependencia::get(); + $roles = Role::get(); + return view('adminGen.usuarios.create', compact('dependencias', 'roles')); + } + + /** + * Store a newly created resource in storage. + */ + public function store(StoreUserRequest $request) + { + $request->validated(); + $user = new User; + $user->name = $request->name; + $user->username = $request->username; + $user->password = Hash::make($request->password); + $user->dependencia_id = $request->dependencia_id; + $user->assignRole($request->role); + if($request->active == "on"){ + $user->active = 1; + }else{ + $user->active = 0; + } + $user->save(); + return redirect()->route('usuarios.get')->with('success', 'Usuario creado correctamente.'); + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + $user = User::findOrFail($id); + $dependencias = Dependencia::all(); + $roles = Role::all(); + return view('adminGen.usuarios.edit', compact('user', 'dependencias', 'roles')); + } + + /** + * Update the specified resource in storage. + */ + public function update(UpdateRequestUser $request, string $id) + { + $request->validated(); + $user = User::findOrFail($id); + + $user->name = $request->name; + $user->username = $request->username; + if ($request->filled('password')) { + $user->password = Hash::make($request->password); + } + $user->dependencia_id = $request->dependencia_id; + $user->assignRole($request->role); + $user->active = $request->has('active'); + $user->save(); + + return redirect()->route('usuarios.get')->with('success', 'Usuario actualizado correctamente.'); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + try{ + $usuario = User::findOrFail($id); + $usuario->delete(); + return redirect()->route('usuarios.get')->with('success', 'Usuario eliminado correctamente.'); + }catch(\Exception $e){ + return redirect()->route('usuarios.get')->withErrors('Error al eliminar el usuario:' . $e->getMessage()); + } + } +} diff --git a/app/Http/Requests/StoreUserRequest.php b/app/Http/Requests/StoreUserRequest.php new file mode 100644 index 0000000000000000000000000000000000000000..d63927af625adbe209660755eae62744d71ed9d2 --- /dev/null +++ b/app/Http/Requests/StoreUserRequest.php @@ -0,0 +1,32 @@ +|string> + */ + public function rules(): array + { + return [ + 'name' => 'required|regex:/\w*$/|string|min:1|max:255', + 'username' => 'required|string|regex:/\w*$/|max:255|unique:users,username', + 'password' => 'required|regex:/\w*$/|string|min:8|max:255|confirmed', + 'dependencia_id' => 'required|min:1|integer', + 'role' => 'required|string|min:1' + ]; + } +} diff --git a/app/Http/Requests/UpdateRequestUser.php b/app/Http/Requests/UpdateRequestUser.php new file mode 100644 index 0000000000000000000000000000000000000000..4ff32c3e3c903cde415ac69db89691bff65d0204 --- /dev/null +++ b/app/Http/Requests/UpdateRequestUser.php @@ -0,0 +1,34 @@ +|string> + */ + public function rules(): array + { + $id = $this->route('id'); + return [ + 'name' => 'required|string|max:255', + 'username' => ['required', 'string', 'max:255', Rule::unique('users')->ignore($id)], + 'password' => 'nullable|string|min:8|confirmed', + 'dependencia_id' => 'required|exists:dependencias,id', + 'role' => 'required|exists:roles,name', + ]; + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 5867318b656a6e5bd4ecec7cf3dac29a22e881ab..a02b9c03371b8f8d3303dd795edc2f0b04bfdb0c 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -4,6 +4,7 @@ // use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Fortify\TwoFactorAuthenticatable; @@ -31,6 +32,7 @@ class User extends Authenticatable 'username', 'active', 'password', + 'dependencia_id' ]; /** @@ -66,4 +68,9 @@ protected function casts(): array 'password' => 'hashed', ]; } + + public function dependencia(): BelongsTo + { + return $this->belongsTo(Dependencia::class); + } } diff --git a/public/assets/css/jquery.toast.css b/public/assets/css/jquery.toast.css new file mode 100644 index 0000000000000000000000000000000000000000..9a41bc220d64a7c47de8bb15df4b22dd87782e47 --- /dev/null +++ b/public/assets/css/jquery.toast.css @@ -0,0 +1,133 @@ +/** + * jQuery toast plugin created by Kamran Ahmed copyright MIT license 2014 + */ +.jq-toast-wrap { + display: block; + position: fixed; + width: 250px; + pointer-events: none !important; + margin: 0; + padding: 0; + letter-spacing: normal; + z-index: 9000 !important; +} +.jq-toast-wrap * { + margin: 0; + padding: 0; +} + +.jq-toast-wrap.bottom-left { + bottom: 20px; + left: 20px; +} +.jq-toast-wrap.bottom-right { + bottom: 20px; + right: 40px; +} +.jq-toast-wrap.top-left { + top: 20px; + left: 20px; +} +.jq-toast-wrap.top-right { + top: 20px; + right: 80px; +} + +.jq-toast-single { + display: block; + width: 100%; + padding: 10px; + margin: 0px 0px 5px; + border-radius: 4px; + font-size: 13px; + font-family: arial, sans-serif; + line-height: 17px; + position: relative; + pointer-events: all !important; + background-color: #444444; + color: white; +} + +.jq-toast-single h2 { + font-family: arial, sans-serif; + font-size: 14px; + margin: 0px 0px 7px; + background: none; + + color: inherit; + line-height: inherit; + letter-spacing: normal; +} +.jq-toast-single a { + color: #eee; + text-decoration: none; + font-weight: bold; + border-bottom: 1px solid white; + padding-bottom: 3px; + font-size: 12px; +} + +.jq-toast-single ul { + margin: 0px 0px 0px 15px; + background: none; + padding:0px; +} +.jq-toast-single ul li { + list-style-type: disc !important; + line-height: 17px; + background: none; + margin: 0; + padding: 0; + letter-spacing: normal; +} + +.close-jq-toast-single { + position: absolute; + top: 3px; + right: 7px; + font-size: 14px; + cursor: pointer; +} + +.jq-toast-loader { + display: block; + position: absolute; + top: -2px; + height: 5px; + width: 0%; + left: 0; + border-radius: 5px; + background: #D1EDD6; +} +.jq-toast-loaded { + width: 100%; +} +.jq-has-icon { + padding: 10px 10px 10px 50px; + background-repeat: no-repeat; + background-position: 10px; +} +.jq-icon-info { + background-image: url(/assets/images/toast/infocircle.png); + background-color: #F1F7FA; + color: #d9edf7; + border-color: #bce8f1; +} +.jq-icon-warning { + background-image: url(/assets/images/toast/warning2.png); + background-color: #FAF0E4; + color: #fcf8e3; + border-color: #faebcc; +} +.jq-icon-error { + background-image: url(/assets/images/toast/error_circle.png); + background-color: #F8E2E1; + color: #676767; + border-color: #676767; +} +.jq-icon-success { + background-image: url(/assets/images/toast/icon_check_circle.png); + background-color: #F1FAF3; + color: #676767; + border-color: #F1FAF3; +} \ No newline at end of file diff --git a/public/assets/images/toast/error_circle.png b/public/assets/images/toast/error_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..f5727de72e25cc01dcd97c0b40f1a3a172849ba4 Binary files /dev/null and b/public/assets/images/toast/error_circle.png differ diff --git a/public/assets/images/toast/icon_check_circle.png b/public/assets/images/toast/icon_check_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..038f5aaeb5fb8f9d492191e2ed3d2d89d424a846 Binary files /dev/null and b/public/assets/images/toast/icon_check_circle.png differ diff --git a/public/assets/images/toast/infocircle.png b/public/assets/images/toast/infocircle.png new file mode 100644 index 0000000000000000000000000000000000000000..c09cad1741d5c694c218236f06c697a85e96e164 Binary files /dev/null and b/public/assets/images/toast/infocircle.png differ diff --git a/public/assets/images/toast/warning2.png b/public/assets/images/toast/warning2.png new file mode 100644 index 0000000000000000000000000000000000000000..4ab9a944d3a1f4cdf252b04c6172716b8a9f3649 Binary files /dev/null and b/public/assets/images/toast/warning2.png differ diff --git a/public/assets/js/jquery.toast.js b/public/assets/js/jquery.toast.js new file mode 100644 index 0000000000000000000000000000000000000000..77cdf45c64bf03e60dda155c94d959c00fe1c0fa --- /dev/null +++ b/public/assets/js/jquery.toast.js @@ -0,0 +1,359 @@ +// jQuery toast plugin created by Kamran Ahmed copyright MIT license 2015 +if ( typeof Object.create !== 'function' ) { + Object.create = function( obj ) { + function F() {} + F.prototype = obj; + return new F(); + }; +} + +(function( $, window, document, undefined ) { + + "use strict"; + + var Toast = { + + _positionClasses : ['bottom-left', 'bottom-right', 'top-right', 'top-left', 'bottom-center', 'top-center', 'mid-center'], + _defaultIcons : ['success', 'error', 'info', 'warning'], + + init: function (options, elem) { + this.prepareOptions(options, $.toast.options); + this.process(); + }, + + prepareOptions: function(options, options_to_extend) { + var _options = {}; + if ( ( typeof options === 'string' ) || ( options instanceof Array ) ) { + _options.text = options; + } else { + _options = options; + } + this.options = $.extend( {}, options_to_extend, _options ); + }, + + process: function () { + this.setup(); + this.addToDom(); + this.position(); + this.bindToast(); + this.animate(); + }, + + setup: function () { + + var _toastContent = ''; + + this._toastEl = this._toastEl || $('
', { + class : 'jq-toast-single' + }); + + // For the loader on top + _toastContent += ''; + + if ( this.options.allowToastClose ) { + _toastContent += '×'; + }; + + if ( this.options.text instanceof Array ) { + + if ( this.options.heading ) { + _toastContent +='

' + this.options.heading + '

'; + }; + + _toastContent += ''; + + } else { + if ( this.options.heading ) { + _toastContent +='

' + this.options.heading + '

'; + }; + _toastContent += this.options.text; + } + + this._toastEl.html( _toastContent ); + + if ( this.options.bgColor !== false ) { + this._toastEl.css("background-color", this.options.bgColor); + }; + + if ( this.options.textColor !== false ) { + this._toastEl.css("color", this.options.textColor); + }; + + if ( this.options.textAlign ) { + this._toastEl.css('text-align', this.options.textAlign); + } + + if ( this.options.icon !== false ) { + this._toastEl.addClass('jq-has-icon'); + + if ( $.inArray(this.options.icon, this._defaultIcons) !== -1 ) { + this._toastEl.addClass('jq-icon-' + this.options.icon); + }; + }; + + if ( this.options.class !== false ){ + this._toastEl.addClass(this.options.class) + } + }, + + position: function () { + if ( ( typeof this.options.position === 'string' ) && ( $.inArray( this.options.position, this._positionClasses) !== -1 ) ) { + + if ( this.options.position === 'bottom-center' ) { + this._container.css({ + left: ( $(window).outerWidth() / 2 ) - this._container.outerWidth()/2, + bottom: 20 + }); + } else if ( this.options.position === 'top-center' ) { + this._container.css({ + left: ( $(window).outerWidth() / 2 ) - this._container.outerWidth()/2, + top: 20 + }); + } else if ( this.options.position === 'mid-center' ) { + this._container.css({ + left: ( $(window).outerWidth() / 2 ) - this._container.outerWidth()/2, + top: ( $(window).outerHeight() / 2 ) - this._container.outerHeight()/2 + }); + } else { + this._container.addClass( this.options.position ); + } + + } else if ( typeof this.options.position === 'object' ) { + this._container.css({ + top : this.options.position.top ? this.options.position.top : 'auto', + bottom : this.options.position.bottom ? this.options.position.bottom : 'auto', + left : this.options.position.left ? this.options.position.left : 'auto', + right : this.options.position.right ? this.options.position.right : 'auto' + }); + } else { + this._container.addClass( 'bottom-left' ); + } + }, + + bindToast: function () { + + var that = this; + + this._toastEl.on('afterShown', function () { + that.processLoader(); + }); + + this._toastEl.find('.close-jq-toast-single').on('click', function ( e ) { + + e.preventDefault(); + + if( that.options.showHideTransition === 'fade') { + that._toastEl.trigger('beforeHide'); + that._toastEl.fadeOut(function () { + that._toastEl.trigger('afterHidden'); + }); + } else if ( that.options.showHideTransition === 'slide' ) { + that._toastEl.trigger('beforeHide'); + that._toastEl.slideUp(function () { + that._toastEl.trigger('afterHidden'); + }); + } else { + that._toastEl.trigger('beforeHide'); + that._toastEl.hide(function () { + that._toastEl.trigger('afterHidden'); + }); + } + }); + + if ( typeof this.options.beforeShow == 'function' ) { + this._toastEl.on('beforeShow', function () { + that.options.beforeShow(); + }); + }; + + if ( typeof this.options.afterShown == 'function' ) { + this._toastEl.on('afterShown', function () { + that.options.afterShown(); + }); + }; + + if ( typeof this.options.beforeHide == 'function' ) { + this._toastEl.on('beforeHide', function () { + that.options.beforeHide(); + }); + }; + + if ( typeof this.options.afterHidden == 'function' ) { + this._toastEl.on('afterHidden', function () { + that.options.afterHidden(); + }); + }; + }, + + addToDom: function () { + + var _container = $('.jq-toast-wrap'); + + if ( _container.length === 0 ) { + + _container = $('
',{ + class: "jq-toast-wrap", + role: "alert", + "aria-live": "polite" + }); + + $('body').append( _container ); + + } else if ( !this.options.stack || isNaN( parseInt(this.options.stack, 10) ) ) { + _container.empty(); + } + + _container.find('.jq-toast-single:hidden').remove(); + + _container.append( this._toastEl ); + + if ( this.options.stack && !isNaN( parseInt( this.options.stack ), 10 ) ) { + + var _prevToastCount = _container.find('.jq-toast-single').length, + _extToastCount = _prevToastCount - this.options.stack; + + if ( _extToastCount > 0 ) { + $('.jq-toast-wrap').find('.jq-toast-single').slice(0, _extToastCount).remove(); + }; + + } + + this._container = _container; + }, + + canAutoHide: function () { + return ( this.options.hideAfter !== false ) && !isNaN( parseInt( this.options.hideAfter, 10 ) ); + }, + + processLoader: function () { + // Show the loader only, if auto-hide is on and loader is demanded + if (!this.canAutoHide() || this.options.loader === false) { + return false; + } + + var loader = this._toastEl.find('.jq-toast-loader'); + + // 400 is the default time that jquery uses for fade/slide + // Divide by 1000 for milliseconds to seconds conversion + var transitionTime = (this.options.hideAfter - 400) / 1000 + 's'; + var loaderBg = this.options.loaderBg; + + var style = loader.attr('style') || ''; + style = style.substring(0, style.indexOf('-webkit-transition')); // Remove the last transition definition + + style += '-webkit-transition: width ' + transitionTime + ' ease-in; \ + -o-transition: width ' + transitionTime + ' ease-in; \ + transition: width ' + transitionTime + ' ease-in; \ + background-color: ' + loaderBg + ';'; + + + loader.attr('style', style).addClass('jq-toast-loaded'); + }, + + animate: function () { + + var that = this; + + this._toastEl.hide(); + + this._toastEl.trigger('beforeShow'); + + if ( this.options.showHideTransition.toLowerCase() === 'fade' ) { + this._toastEl.fadeIn(function ( ){ + that._toastEl.trigger('afterShown'); + }); + } else if ( this.options.showHideTransition.toLowerCase() === 'slide' ) { + this._toastEl.slideDown(function ( ){ + that._toastEl.trigger('afterShown'); + }); + } else { + this._toastEl.show(function ( ){ + that._toastEl.trigger('afterShown'); + }); + } + + if (this.canAutoHide()) { + + var that = this; + + window.setTimeout(function(){ + + if ( that.options.showHideTransition.toLowerCase() === 'fade' ) { + that._toastEl.trigger('beforeHide'); + that._toastEl.fadeOut(function () { + that._toastEl.trigger('afterHidden'); + }); + } else if ( that.options.showHideTransition.toLowerCase() === 'slide' ) { + that._toastEl.trigger('beforeHide'); + that._toastEl.slideUp(function () { + that._toastEl.trigger('afterHidden'); + }); + } else { + that._toastEl.trigger('beforeHide'); + that._toastEl.hide(function () { + that._toastEl.trigger('afterHidden'); + }); + } + + }, this.options.hideAfter); + }; + }, + + reset: function ( resetWhat ) { + + if ( resetWhat === 'all' ) { + $('.jq-toast-wrap').remove(); + } else { + this._toastEl.remove(); + } + + }, + + update: function(options) { + this.prepareOptions(options, this.options); + this.setup(); + this.bindToast(); + } + }; + + $.toast = function(options) { + var toast = Object.create(Toast); + toast.init(options, this); + + return { + + reset: function ( what ) { + toast.reset( what ); + }, + + update: function( options ) { + toast.update( options ); + } + } + }; + + $.toast.options = { + text: '', + heading: '', + showHideTransition: 'fade', + allowToastClose: true, + hideAfter: 3000, + loader: true, + loaderBg: '#9EC600', + stack: 5, + position: 'bottom-left', + bgColor: false, + textColor: false, + textAlign: 'left', + icon: false, + beforeShow: function () {}, + afterShown: function () {}, + beforeHide: function () {}, + afterHidden: function () {} + }; + +})( jQuery, window, document ); diff --git a/resources/views/adminGen/catalogos/dependencias.blade.php b/resources/views/adminGen/catalogos/dependencias.blade.php index 8892cf7a5c04b19a55cc5477ea5803980e0e05bb..d3cb85fa7b1733069227a281463b97685a889afb 100644 --- a/resources/views/adminGen/catalogos/dependencias.blade.php +++ b/resources/views/adminGen/catalogos/dependencias.blade.php @@ -213,63 +213,29 @@ function editItem(id, nombre) { })); }); - showMessage = (msg = 'Example notification text.', position = 'top-end', showCloseButton = true, closeButtonHtml = '', duration = 3000, type = 'success') => { - const toast = window.Swal.mixin({ - toast: true, - position: position || 'top-end', - showConfirmButton: false, - timer: duration, - showCloseButton: showCloseButton, - customClass: { - container: 'custom-swal-container', - popup: 'custom-swal-popup', - title: 'custom-swal-title', - } - }); - - // Define el color y el icono según el tipo de mensaje - let color; - let icon; - if (type === 'success') { - color = '#4caf50'; // Color verde para éxito - icon = 'success'; // Icono de éxito - } else if (type === 'error') { - color = '#f44336'; // Color rojo para error - icon = 'error'; // Icono de error - } - - // Aplica el estilo al mensaje - toast.fire({ - title: msg, + function showToast(message, heading, icon) { + $.toast({ + heading: heading, + text: message, + showHideTransition: 'slide', icon: icon, - showClass: { - popup: 'swal2-noanimation', - }, - hideClass: { - popup: '', - }, - customClass: { - title: 'swal2-title', - popup: 'swal2-popup', - icon: 'swal2-icon', - }, - timerProgressBar: true, - timerProgressBarColor: color, - timerProgressBarHeight: 3, - background: color, - iconColor: 'white', + position: 'top-right', + loader: false, + hideAfter: 10000, + allowToastClose: true, + textColor: '#676767', }); - }; + } document.addEventListener('DOMContentLoaded', function() { @if(session('success')) // Muestra el mensaje de éxito utilizando showMessage - showMessage("{{ session('success') }}", 'top-end', true, '', 3000, 'success'); + showToast("{{ session('success') }}", 'Exito.', 'success') @endif - @if($errors -> any()) - @foreach($errors -> all() as $error) - showMessage("{{$error}}", 'top-end', true, '', 3000, 'error'); + @if($errors->any()) + @foreach($errors->all() as $error) + showToast(`{{ $error }}`, 'Error.', 'error'); @endforeach @endif }); diff --git a/resources/views/adminGen/usuarios/create.blade.php b/resources/views/adminGen/usuarios/create.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..76fd255cc7e0970cac324beaaa1bb697028a7506 --- /dev/null +++ b/resources/views/adminGen/usuarios/create.blade.php @@ -0,0 +1,166 @@ + + + + +
+ +
+
+
Crear nuevo usuario
+
+
+
+ @csrf +
+
+ + +
+
+ +
+
+ @
+ +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+ +
+ + +
+
+ ¿Activar usuario? + +
+
+ +
+
+
+
+ + + + + + + + + + +
\ No newline at end of file diff --git a/resources/views/adminGen/usuarios/edit.blade.php b/resources/views/adminGen/usuarios/edit.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..9920218a7d66991079c8fc5090544a5b3c216ed1 --- /dev/null +++ b/resources/views/adminGen/usuarios/edit.blade.php @@ -0,0 +1,166 @@ + + + + +
+ +
+
+
Editar usuario
+
+
+
+ @csrf + @method('PUT') +
+
+ + +
+
+ +
+
@
+ +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+ +
+ + +
+
+ ¿Activar usuario? + +
+
+ +
+
+
+
+ + + + + + + + + + +
\ No newline at end of file diff --git a/resources/views/adminGen/usuarios/index.blade.php b/resources/views/adminGen/usuarios/index.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..b93f4be15ca23edcbeae7dc296e68b097aa61192 --- /dev/null +++ b/resources/views/adminGen/usuarios/index.blade.php @@ -0,0 +1,122 @@ + + + + + +
+ + +
+ + + + + + + + + +
\ No newline at end of file diff --git a/routes/web.php b/routes/web.php index d69db524ea18325af715fa8f6b4a47825eb6b2a5..b5300ff920b67dd8fa21e2e7f7b60602e96d3b5b 100644 --- a/routes/web.php +++ b/routes/web.php @@ -7,6 +7,7 @@ use App\Http\Controllers\CargoController; use App\Http\Controllers\CaracteristicasController; use App\Http\Controllers\DependenciaController; +use App\Http\Controllers\UserController; Route::get('/', function () { return view('welcome'); @@ -41,3 +42,12 @@ Route::put('/catalogos/dependencias', [DependenciaController::class, 'update'])->name('dependencias.update'); Route::delete('/catalogos/dependencias/{id}', [DependenciaController::class, 'destroy'])->name('dependencias.destroy'); }); + +Route::name('usuarios.')->group(function () { + Route::get('/administracion/usuarios', [UserController::class, 'index'])->name('get'); + Route::get('/administracion/usuarios/crear', [UserController::class, 'create'])->name('create'); + Route::post('/administracion/usuarios/crear', [UserController::class, 'store'])->name('store'); + Route::get('/administracion/usuarios/{id}/editar', [UserController::class, 'edit'])->name('edit'); + Route::put('/administracion/usuarios/{id}/editar', [UserController::class, 'update'])->name('update'); + Route::delete('/administracion/usuarios/{id}', [UserController::class, 'destroy'])->name('destroy'); +});