Add rank management feature with CRUD and UI

Introduces a new rank management system including the Rank model, migrations for the ranks table and user IP address, Livewire components for managing and displaying ranks, and related Blade views. Updates navigation and routes to include the new 'Gestión Rangos' section, and enhances the ShareCodeComponent with user data. Also adds a reusable input field component for forms.
This commit is contained in:
Juan Felipe Duarte
2025-10-23 20:28:47 -05:00
parent aa67099449
commit a558e393f2
17 changed files with 522 additions and 6 deletions
+4
View File
@@ -42,6 +42,10 @@ class MenuController extends Controller
{
return view('menu/tarifas');
}
public function showgestionrangos()
{
return view('menu/gestion-rangos');
}
public function showclientes()
{
return view('menu/clientes');
+7
View File
@@ -7,6 +7,13 @@ use Livewire\Component;
class ShareCodeComponent extends Component
{
public $user;
public function mount()
{
$this->user = Auth::user();
}
public function render()
{
$referrals = Auth::user()->children()
+132
View File
@@ -0,0 +1,132 @@
<?php
namespace App\Http\Livewire;
use App\Models\Rank;
use Livewire\Component;
use Livewire\WithFileUploads;
class ShowGestionRangos extends Component
{
use WithFileUploads;
public $ranks;
public $nombre, $cashback, $bono_equipo, $tope_bono_equipo;
public $compra_personal_min, $directos_activos_min, $niveles_min, $volumen_equipo_min, $volumen_por_nivel_min;
public $rank_id;
public $logo;
public $logo_url;
public $modal = false;
protected $rules = [
'nombre' => 'required|string|max:100',
'cashback' => 'required|numeric|min:0',
'bono_equipo' => 'required|numeric|min:0',
'tope_bono_equipo' => 'required|numeric|min:0',
'compra_personal_min' => 'required|numeric|min:0',
'directos_activos_min' => 'required|integer|min:0',
'niveles_min' => 'required|integer|min:0',
'volumen_equipo_min' => 'required|numeric|min:0',
'volumen_por_nivel_min' => 'required|numeric|min:0',
'logo' => 'nullable|image|max:2048',
];
protected $messages = [
'nombre.required' => 'El nombre del rango es obligatorio.',
'nombre.string' => 'El nombre debe ser una cadena de texto válida.',
'nombre.max' => 'El nombre no puede tener más de 100 caracteres.',
'cashback.required' => 'El porcentaje de cashback es obligatorio.',
'cashback.numeric' => 'El cashback debe ser un valor numérico.',
'cashback.min' => 'El cashback no puede ser menor a 0.',
'bono_equipo.required' => 'El porcentaje de bono de equipo es obligatorio.',
'bono_equipo.numeric' => 'El bono de equipo debe ser un valor numérico.',
'bono_equipo.min' => 'El bono de equipo no puede ser menor a 0.',
'tope_bono_equipo.required' => 'El tope del bono de equipo es obligatorio.',
'tope_bono_equipo.numeric' => 'El tope del bono de equipo debe ser un valor numérico.',
'tope_bono_equipo.min' => 'El tope del bono de equipo no puede ser menor a 0.',
'compra_personal_min.required' => 'La compra personal mínima es obligatoria.',
'compra_personal_min.numeric' => 'La compra personal mínima debe ser un número.',
'compra_personal_min.min' => 'La compra personal mínima no puede ser menor a 0.',
'directos_activos_min.required' => 'El número de directos activos mínimos es obligatorio.',
'directos_activos_min.integer' => 'Los directos activos deben ser un número entero.',
'directos_activos_min.min' => 'Los directos activos no pueden ser menores a 0.',
'niveles_min.required' => 'El número de niveles mínimos es obligatorio.',
'niveles_min.integer' => 'Los niveles mínimos deben ser un número entero.',
'niveles_min.min' => 'Los niveles mínimos no pueden ser menores a 0.',
'volumen_equipo_min.required' => 'El volumen mínimo de equipo es obligatorio.',
'volumen_equipo_min.numeric' => 'El volumen mínimo de equipo debe ser un número.',
'volumen_equipo_min.min' => 'El volumen mínimo de equipo no puede ser menor a 0.',
'volumen_por_nivel_min.required' => 'El volumen por nivel mínimo es obligatorio.',
'volumen_por_nivel_min.numeric' => 'El volumen por nivel mínimo debe ser un número.',
'volumen_por_nivel_min.min' => 'El volumen por nivel mínimo no puede ser menor a 0.',
'logo.image' => 'El archivo del logo debe ser una imagen válida (JPG, PNG, etc.).',
'logo.max' => 'El logo no puede superar los 2 MB.',
];
public function render()
{
$this->ranks = Rank::orderBy('volumen_equipo_min')->get();
return view('livewire.show-gestion-rangos');
}
public function crear()
{
$this->resetExcept('ranks');
$this->modal = true;
}
public function editar($id)
{
$rank = Rank::findOrFail($id);
$this->fill($rank->toArray());
$this->rank_id = $rank->id;
$this->modal = true;
}
public function guardar()
{
$this->validate();
$rutaLogo = null;
if ($this->logo) {
$rutaLogo = $this->logo->store('photos');
}
Rank::updateOrCreate(
['id' => $this->rank_id],
[
'nombre' => $this->nombre,
'cashback' => $this->cashback,
'bono_equipo' => $this->bono_equipo,
'tope_bono_equipo' => $this->tope_bono_equipo,
'compra_personal_min' => $this->compra_personal_min,
'directos_activos_min' => $this->directos_activos_min,
'niveles_min' => $this->niveles_min,
'volumen_equipo_min' => $this->volumen_equipo_min,
'volumen_por_nivel_min' => $this->volumen_por_nivel_min,
'logo' => $rutaLogo ?? $this->logo_url,
]
);
$this->modal = false;
$this->resetExcept('ranks');
}
public function eliminar($id)
{
Rank::findOrFail($id)->delete();
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class ShowRangos extends Component
{
public function render()
{
return view('livewire.show-rangos');
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ use Illuminate\Support\Facades\Auth;
class ShowSavingIp extends Component
{
public $ip_address;
public $ip_address;
protected $listeners = ['setIpAddress'];