update codigo referido funcionamiento login, register y dashboard

This commit is contained in:
Juan Felipe Duarte
2025-10-15 16:43:43 -05:00
parent 6d49224d44
commit 5c04a95792
10 changed files with 490 additions and 239 deletions
@@ -3,6 +3,8 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Role;
use App\Models\Saldo;
use App\Models\User; use App\Models\User;
use App\Providers\RouteServiceProvider; use App\Providers\RouteServiceProvider;
use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Events\Registered;
@@ -13,36 +15,69 @@ use Illuminate\Validation\Rules;
class RegisteredUserController extends Controller class RegisteredUserController extends Controller
{ {
/** public function create(Request $request)
* Display the registration view.
*
* @return \Illuminate\View\View
*/
public function create()
{ {
return view('auth.register'); $referralCode = $request->query('ref');
return view('auth.register', [
'referralCode' => $referralCode,
]);
} }
/**
* Handle an incoming registration request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request) public function store(Request $request)
{ {
$request->validate([ $request->validate([
'name' => ['required', 'string', 'max:255'], 'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'confirmed', Rules\Password::defaults()], 'password' => ['required', 'confirmed', Rules\Password::defaults()],
'referral_code' => ['required', 'string'],
],[
'email.unique' => 'El correo electrónico ya está en uso.',
'email.email' => 'El correo electrónico no es válido.',
'email.required' => 'El correo electrónico es obligatorio.',
'name.required' => 'El nombre es obligatorio.',
'name.string' => 'El nombre debe ser una cadena de texto.',
'name.max' => 'El nombre no debe exceder los 255 caracteres.',
'password.required' => 'La contraseña es obligatoria.',
'password.confirmed' => 'La confirmación de la contraseña no coincide.',
'password.min' => 'La contraseña debe tener al menos 8 caracteres.',
'referral_code.required' => 'El código de referido es obligatorio.',
'password.confirmed' => 'Las contraseñas no coinciden.',
]); ]);
$referralUser = null;
if ($request->filled('referral_code')) {
$referralUser = User::where('referral_code', $request->referral_code)->first();
}
// Determinar el rol del nuevo usuario
if ($referralUser) {
if ($referralUser->rol->nombre === 'super') {
// Si el referido es "super", asignar "cliente"
$rolCliente = Role::where('nombre', 'cliente')->first();
$rol_id = $rolCliente ? $rolCliente->id : null;
} else {
// Si no es super, tomar el mismo rol del referido
$rol_id = $referralUser->rol_id;
}
} else {
// Si no tiene referral_code, asignar rol por defecto (cliente)
$rolCliente = Role::where('nombre', 'cliente')->first();
$rol_id = $rolCliente ? $rolCliente->id : null;
}
$user = User::create([ $user = User::create([
'name' => $request->name, 'name' => $request->name,
'email' => $request->email, 'email' => $request->email,
'password' => Hash::make($request->password), 'password' => Hash::make($request->password),
'rol_id' => $rol_id,
'parent_id' => $referralUser ? $referralUser->id : null,
]);
Saldo::create([
'usuario_id' => $user->id,
'valor' => 0,
]); ]);
event(new Registered($user)); event(new Registered($user));
+20
View File
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Livewire;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;
class ShareCodeComponent extends Component
{
public function render()
{
$referrals = Auth::user()->children()
->select('*')
->selectRaw('(SELECT COUNT(*) FROM users u WHERE u.parent_id = users.id::text) as children_count')
->orderBy('created_at', 'desc')
->get();
return view('livewire.share-code-component', compact('referrals'));
}
}
+39
View File
@@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens; use Laravel\Sanctum\HasApiTokens;
use Illuminate\Support\Str;
class User extends Authenticatable class User extends Authenticatable
{ {
@@ -22,6 +23,12 @@ class User extends Authenticatable
'rol_id', 'rol_id',
'subvendedor_id', 'subvendedor_id',
'subvendedor', 'subvendedor',
'referral_code',
'parent_id',
'rank_id',
]; ];
protected $hidden = [ protected $hidden = [
@@ -107,4 +114,36 @@ class User extends Authenticatable
{ {
return $this->hasMany(User::class, 'subvendedor_id'); return $this->hasMany(User::class, 'subvendedor_id');
} }
// Referral relationships
public function parent()
{
return $this->belongsTo(User::class, 'parent_id');
}
public function children()
{
return $this->hasMany(User::class, 'parent_id')->orderBy('created_at', 'desc');
}
// public function rank()
// {
// return $this->belongsTo(Rank::class);
// }
protected static function boot()
{
parent::boot();
static::creating(function ($user) {
if (empty($user->referral_code)) {
do {
$code = strtoupper(Str::random(8));
} while (self::where('referral_code', $code)->exists());
$user->referral_code = $code;
}
});
}
} }
@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('referral_code')->unique()->nullable();
$table->string('parent_id')->nullable();
$table->string('rank_id')->nullable();
});
}
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn([
'referral_code',
'parent_id',
'rank_id',
]);
});
}
};
+10
View File
@@ -62,6 +62,16 @@
</a> </a>
@endif @endif
</div> </div>
<div class="mt-4 text-center">
<span class="text-sm text-gray-600 md:text-base">
¿No tienes una cuenta?
</span>
<a href="{{ route('register') }}"
class="inline-block ml-2 px-4 py-2 bg-[#B221FD] hover:bg-[#7a02b8] text-white text-sm md:text-base font-semibold rounded-md shadow-md transition-all duration-200">
Crear cuenta
</a>
</div>
</form> </form>
</div> </div>
+52 -25
View File
@@ -1,64 +1,91 @@
<x-guest-layout> <x-guest-layout>
<x-auth-card> <x-auth-session-status class="mb-4" :status="session('status')" />
<x-slot name="logo">
<a href="/">
<x-application-logo class="w-20 h-20 fill-current text-gray-500" />
</a>
</x-slot>
<form method="POST" action="{{ route('register') }}"> <div
class="h-full bg-[#000029] overflow-y-auto md:grid md:grid-cols-2 text-center content-center items-center w-full md:flex md:px-5">
<div
class="border mt-2 mb-0 md:mb-8 md:mt-0 mx-auto md:mr-14 border-gray-600/20 rounded-full w-[15rem] md:w-full md:max-w-xl overflow-hidden relative ">
<img src="{{ App\Models\Configuracione::Orderby('id', 'desc')->first()->url_logo }}" alt=""
class="">
<div class="text-lg text-[#d37bff] w-full absolute bottom-6 md:bottom-10 text-center">
<a href="#">@ {{ App\Models\Configuracione::Orderby('id', 'desc')->first()->nombre }}</a>
</div>
</div>
<div
class="mx-auto mt-[-1.5rem] mb-[3.5rem] md:mb-0 flex justify-center w-[17rem] md:max-w-sm md:w-full bg-[#F2F4FF] md:ml-28 px-6 py-5 md:py-10 rounded-lg select-none">
<form method="POST" action="{{ route('register') }}" class="w-full px-4">
@csrf @csrf
<!-- Name --> <!-- Name -->
<div> <div>
<x-input-label for="name" :value="__('Name')" /> {{-- <x-input-label for="name" :value="__('Name')" /> --}}
<x-text-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus /> <x-text-input id="name" class="block mt-1 w-full" type="text" name="name"
placeholder="Ingresa tu nombre"
:value="old('name')" required autofocus />
<x-input-error :messages="$errors->get('name')" class="mt-2" /> <x-input-error :messages="$errors->get('name')" class="mt-2" />
</div> </div>
<!-- Email Address --> <!-- Email Address -->
<div class="mt-4"> <div class="mt-4">
<x-input-label for="email" :value="__('Email')" /> {{-- <x-input-label for="email" :value="__('Email')" /> --}}
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required /> <x-text-input id="email" class="block mt-1 w-full" type="email" name="email" placeholder="ejemplo@correo.com"
:value="old('email')" required />
<x-input-error :messages="$errors->get('email')" class="mt-2" /> <x-input-error :messages="$errors->get('email')" class="mt-2" />
</div> </div>
<!-- Password --> <!-- Password -->
<div class="mt-4"> <div class="mt-4">
<x-input-label for="password" :value="__('Password')" /> {{-- <x-input-label for="password" :value="__('Password')" /> --}}
<x-text-input id="password" class="block mt-1 w-full" <x-text-input id="password" class="block mt-1 w-full" type="password" name="password" required placeholder="Crea una contraseña segura"
type="password" autocomplete="new-password" />
name="password"
required autocomplete="new-password" />
<x-input-error :messages="$errors->get('password')" class="mt-2" /> <x-input-error :messages="$errors->get('password')" class="mt-2" />
</div> </div>
<!-- Confirm Password --> <!-- Confirm Password -->
<div class="mt-4"> <div class="mt-4">
<x-input-label for="password_confirmation" :value="__('Confirm Password')" /> {{-- <x-input-label for="password_confirmation" :value="__('Confirm Password')" /> --}}
<x-text-input id="password_confirmation" class="block mt-1 w-full" <x-text-input id="password_confirmation" class="block mt-1 w-full" type="password" placeholder="Repite tu contraseña"
type="password"
name="password_confirmation" required /> name="password_confirmation" required />
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" /> <x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
</div> </div>
<div class="flex items-center justify-end mt-4"> <div class="mt-4">
<a class="underline text-sm text-gray-600 hover:text-gray-900" href="{{ route('login') }}"> {{-- <x-input-label for="referral_code" :value="__('Referral Code')" /> --}}
{{ __('Already registered?') }}
@if (!empty($referralCode))
<x-text-input id="referral_code" class="block mt-1 w-full" type="text" name="referral_code"
value="{{ old('referral_code', $referralCode) }}" required autocomplete="referral_code" placeholder="Código de referido"
readonly />
@else
<x-text-input id="referral_code" class="block mt-1 w-full" type="text" name="referral_code" placeholder="Código de referido"
value="{{ old('referral_code') }}" autocomplete="referral_code" />
@endif
<x-input-error :messages="$errors->get('referral_code')" class="mt-2" />
</div>
<div class="flex flex-col md:flex-row items-center justify-between mt-6 space-y-3 md:space-y-0">
<a href="{{ route('login') }}"
class="w-full md:w-auto text-center px-4 py-2 bg-gray-200 hover:bg-gray-300 text-gray-700 text-sm md:text-base font-semibold rounded-md shadow-sm transition-all duration-200">
{{ __('Iniciar Sesión') }}
</a> </a>
<x-primary-button class="ml-4"> <x-primary-button class="w-full md:w-auto justify-center text-sm md:text-base">
{{ __('Register') }} {{ __('Registrarse') }}
</x-primary-button> </x-primary-button>
</div> </div>
</form> </form>
</x-auth-card> </div>
</div>
</x-guest-layout> </x-guest-layout>
+6 -2
View File
@@ -1,8 +1,12 @@
<x-app-layout> <x-app-layout>
<livewire:show-inicio /> <livewire:show-inicio />
<br>
@if (!in_array(auth()->user()->rol?->nombre, ['administrador', 'Roger','editor']))
<livewire:share-code-component />
@endif
<br> <br>
<livewire:show-clientes />
</x-app-layout> </x-app-layout>
@@ -0,0 +1,107 @@
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 my-12">
<div>
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
<div class="px-4 py-5 sm:px-6">
<h3 class="text-lg leading-6 font-medium text-gray-900">Compartir Código</h3>
<p class="mt-1 max-w-2xl text-sm text-gray-500">
Comparte tu código de referido.
</p>
<div class="mt-4 p-4 bg-gray-100 rounded">
<span class="text-sm text-gray-700">Tu código:</span>
<div class="flex items-center mt-2">
<input type="text" id="referralLink" value="{{ route('register', ['ref' => Auth::user()->referral_code]) }}" class="flex-1 px-3 py-2 border border-gray-300 rounded-l-md text-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" readonly >
<button onclick="copyToClipboard()" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-r-md text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" >
Copiar
</button>
</div>
<p id="copyFeedback" class="hidden text-green-600 text-xs mt-1"></p>
</div>
</div>
</div>
</div>
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mb-12">
<div class="bg-white">
<div class="flex items-center justify-between mb-4">
<div class="px-4 py-5 sm:px-6">
<h3 class="text-lg leading-6 font-medium text-gray-900">Mis Referidos</h3>
<p class="mt-1 text-sm text-gray-500">
Aquí puedes ver la lista de tus referidos.
</p>
</div>
<div class="flex items-center space-x-2 bg-purple-50 px-4 py-2 rounded-lg shadow-sm">
<svg class="w-6 h-6 text-purple-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z" />
</svg>
<span class="text-purple-700 font-semibold text-lg">
{{ $referrals->count() }}
</span>
<span class="text-gray-600 text-sm">Total de referidos</span>
</div>
</div>
<div class="shadow overflow-hidden sm:rounded-lg">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
Fecha
</th>
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
Usuario
</th>
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-600 uppercase tracking-wider">
Referidos del usuario
</th>
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-600 uppercase tracking-wider">
Estado
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-100">
@forelse ($referrals as $item)
<tr class="hover:bg-gray-50 transition duration-150">
<td class="px-6 py-4 text-sm text-gray-700">{{ $item->created_at->format('Y-m-d') }}</td>
<td class="px-6 py-4 text-sm font-medium text-gray-900">{{ $item->email }}</td>
<td class="px-6 py-4 text-center text-sm text-gray-700">{{ $item->children_count }}</td>
<td class="px-6 py-4 text-center select-none">
<span class="px-3 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-700">
Inactivo
</span>
</td>
</tr>
@empty
<tr>
<td colspan="4" class="px-6 py-4 text-center text-sm text-gray-500">
No tienes referidos aún.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
function copyToClipboard() {
const copyText = document.getElementById("referralLink");
copyText.select();
copyText.setSelectionRange(0, 99999);
document.execCommand("copy");
const feedback = document.getElementById("copyFeedback");
feedback.classList.remove("hidden");
setTimeout(() => {
feedback.classList.add("hidden");
}, 2000);
}
</script>
@@ -66,67 +66,32 @@
</div> --}} </div> --}}
</div> </div>
<div <div class="swiper-container overflow-hidden py-4 px-3" wire:ignore>
class="x-auto bg-white grid grid-cols-1 lg:grid-cols-1text-center rounded-xl py-4 px-2 ms:px-8 shadow-md xl:mx-10 mb-8 "> <div class="swiper-wrapper">
<div class="mb-2"> @foreach ($categorias as $i => $categoria1)
<h2>Seleccione una categoria:</h2> <div wire:key="banner-{{ $categoria1->id }}" class="swiper-slide cursor-pointer transition hover:scale-105 rounded-lg overflow-hidden shadow bg-white" x-on:click="categorian='{{ $categoria1->nombre }}'">
<img src="{{ asset($categoria1->imagen) }}" class="w-full h-12 md:h-32 object-cover object-center text-xs" alt="imagen de {{ $categoria1->nombre }}">
</div> </div>
<div class="relative">
<div class="slider2 scroll-smooth py-2 px-4 overflow-x-scroll w-full gap-4 " id="content">
<table>
@foreach ($categorias as $categoria1)
<td>
<div class="cursor-pointer transition hover:scale-[1.05] w-[250px] h-[250px] sm:w-[350px] sm:h-[350px] mx-4" x-on:click="categorian='{{ $categoria1->nombre }}'">
<img src="{{ asset($categoria1->imagen) }}" class="w-full m-auto rounded-lg object-cover h-full object-center shadow" alt="imagen de {{ $categoria1->nombre }}">
</div>
</td>
@endforeach @endforeach
</table>
</div> </div>
<button class="prev2 p-2 md:p-3 absolute left-0 top-1/3 rounded-full bg-[#B221FD]"><svg
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:svgjs="http://svgjs.com/svgjs" version="1.1" class="w-4 md:w-7" x="0"
y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512"
xml:space="preserve">
<g>
<path
d="M17.921,1.505a1.5,1.5,0,0,1-.44,1.06L9.809,10.237a2.5,2.5,0,0,0,0,3.536l7.662,7.662a1.5,1.5,0,0,1-2.121,2.121L7.688,15.9a5.506,5.506,0,0,1,0-7.779L15.36.444a1.5,1.5,0,0,1,2.561,1.061Z"
fill="#ffffff" data-original="#000000" />
</g>
</svg></button>
<button class="next2 p-2 md:p-3 absolute right-0 top-1/3 rounded-full bg-[#B221FD]"><svg
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:svgjs="http://svgjs.com/svgjs" version="1.1" class="w-4 md:w-7" x="0"
y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512"
xml:space="preserve">
<g>
<path
d="M6.079,22.5a1.5,1.5,0,0,1,.44-1.06l7.672-7.672a2.5,2.5,0,0,0,0-3.536L6.529,2.565A1.5,1.5,0,0,1,8.65.444l7.662,7.661a5.506,5.506,0,0,1,0,7.779L8.64,23.556A1.5,1.5,0,0,1,6.079,22.5Z"
fill="#ffffff" data-original="#000000" />
</g>
</svg></button>
</div> </div>
<button x-on:click="verPro=true"
class="my-2 mx max-w-sm ml-auto rounded-lg border border-transparent px-4 py-2 sm:mb-[0] mb-2 text-white bg-[#B221FD] hover:bg-[#7a02b8] focus:shadow-outline-green sm-text-sm sm-leading-5">Ver
todos</button> <div class="flex items-center justify-center mt-2 mb-4">
<button x-on:click="verPro=true" class="max-w-md rounded-lg text-sm md:text-base px-4 py-2 text-white bg-[#B221FD] hover:bg-[#7a02b8] focus:shadow-outline-green sm-text-sm sm-leading-5">
Ver todas las promociones
</button>
</div> </div>
<div> <div>
@if ($categorian) @if ($categorian)
<div class="bg-[#f8f8f8] shadow-md rounded-[1.2rem] mb-4 px-4 py-4 "> <div class="mb-4 px-4 py-4">
<div class="mb-2"> <div id="promo" class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 py-2 px-4">
<h2>Seleccione una promocion:</h2>
</div>
<div id="promo" class="grid sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-4 gap-4 py-2 px-4">
@foreach ($categorias->where('nombre', $categorian)->first()->promociones as $promocion) @foreach ($categorias->where('nombre', $categorian)->first()->promociones as $promocion)
@if ($promocion->visible == 'true') @if ($promocion->visible == 'true')
@if (empty(($usuario = $promocion->usuario_promos->where('usuario_id', Auth::user()->id)->first()))) @if (empty(($usuario = $promocion->usuario_promos->where('usuario_id', Auth::user()->id)->first())))
<div class="select-none bg-white shadow-md transition hover:scale-[1.05] hover:shadow-xl cursor-pointer rounded-lg text-center relative object-cover " <div class="select-none bg-white shadow-md transition hover:scale-105 hover:shadow-xl cursor-pointer rounded-lg text-center relative object-cover "
x-data="{ openOption: false }"> x-data="{ openOption: false }">
<svg x-on:click="openOption=!openOption" xmlns="http://www.w3.org/2000/svg" <svg x-on:click="openOption=!openOption" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
@@ -215,8 +180,7 @@
<div x-cloak x-show="newPromocion" x-transition:enter="transition duration-350" <div x-cloak x-show="newPromocion" x-transition:enter="transition duration-350"
x-transition:enter-start="opacity-0 scale-100" x-transition:enter-end="opacity-100 scale-100" x-transition:enter-start="opacity-0 scale-100" x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition duration-350" x-transition:leave-start="opacity-100 scale-100" x-transition:leave="transition duration-350" x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-100" x-transition:leave-end="opacity-0 scale-100" class="fixed inset-0 z-50 backdrop-blur-sm bg-black/20">
class="fixed inset-0 z-50 backdrop-blur-sm bg-black/20">
<div class="grid h-full place-items-center overflow-y-auto"> <div class="grid h-full place-items-center overflow-y-auto">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 rounded-[1rem] shadow-md"> <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 rounded-[1rem] shadow-md">
<div class="mb-4 grid grid-cols-2 items-center justify-items-center"> <div class="mb-4 grid grid-cols-2 items-center justify-items-center">
@@ -291,10 +255,9 @@
</div> </div>
<div class="mb-4"> <div class="mb-4">
<label for="utilidad" <label for="utilidad" class="block text-gray-700 text-sm font-bold mb-2">Valor de
class="block text-gray-700 text-sm font-bold mb-2">Valor de compra:</label> compra:</label>
<input name="utilidad" wire:model="utilidad" type="number" min="0" <input name="utilidad" wire:model="utilidad" type="number" min="0" placeholder="$0"
placeholder="$0"
class="w-full border-2 border-neutral-200 shadow rounded-[0.8rem] px-4 py-2 text-gray-900 sm:text-sm focus:border-neutral-300 focus:outline-none focus:shadow-outline focus:ring-0 block datepicker-input"> class="w-full border-2 border-neutral-200 shadow rounded-[0.8rem] px-4 py-2 text-gray-900 sm:text-sm focus:border-neutral-300 focus:outline-none focus:shadow-outline focus:ring-0 block datepicker-input">
</div> </div>
@@ -327,8 +290,7 @@
class="w-10 h-10 float-right bg-[#B221FD] hover:bg-[#7a02b8] rounded-full active:shadow-lg mouse shadow ease-in focus:outline-none"> class="w-10 h-10 float-right bg-[#B221FD] hover:bg-[#7a02b8] rounded-full active:shadow-lg mouse shadow ease-in focus:outline-none">
<svg viewBox="0 0 20 20" enable-background="new 0 0 20 20" <svg viewBox="0 0 20 20" enable-background="new 0 0 20 20"
class="w-6 h-6 inline-block"> class="w-6 h-6 inline-block">
<path fill="#FFFFFF" <path fill="#FFFFFF" d="M16,10c0,0.553-0.048,1-0.601,1H11v4.399C11,15.951,10.553,16,10,16c-0.553,0-1-0.049-1-0.601V11H4.601
d="M16,10c0,0.553-0.048,1-0.601,1H11v4.399C11,15.951,10.553,16,10,16c-0.553,0-1-0.049-1-0.601V11H4.601
C4.049,11,4,10.553,4,10c0-0.553,0.049-1,0.601-1H9V4.601C9,4.048,9.447,4,10,4c0.553,0,1,0.048,1,0.601V9h4.399 C4.049,11,4,10.553,4,10c0-0.553,0.049-1,0.601-1H9V4.601C9,4.048,9.447,4,10,4c0.553,0,1,0.048,1,0.601V9h4.399
C15.952,9,16,9.447,16,10z" /> C15.952,9,16,9.447,16,10z" />
</svg> </svg>
@@ -356,7 +318,6 @@
{{ $tarifa->pantallas == 1 ? 'pantalla' : 'pantallas' }} - {{ $tarifa->pantallas == 1 ? 'pantalla' : 'pantallas' }} -
{{ $tarifa->dias ?? '' }} {{ $tarifa->dias ?? '' }}
{{ $tarifa->dias == 1 ? 'día' : 'días' }} {{ $tarifa->dias == 1 ? 'día' : 'días' }}
@endif @endif
</option> </option>
@endif @endif
@@ -523,7 +484,9 @@
<div <div
class="bg-white px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse sm:justify-evenly gap-5 mt-2 mb-3"> class="bg-white px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse sm:justify-evenly gap-5 mt-2 mb-3">
<span> <span>
<button x-on:click="modalComprarPromo=true,verPromo=false" wire:loading.attr="disabled" type="button" class="inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 sm:mb-[0] mb-2 text-white bg-[#B221FD] hover:bg-[#7a02b8] focus:shadow-outline-green sm-text-sm sm-leading-5">Comprar</button> <button x-on:click="modalComprarPromo=true,verPromo=false"
wire:loading.attr="disabled" type="button"
class="inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 sm:mb-[0] mb-2 text-white bg-[#B221FD] hover:bg-[#7a02b8] focus:shadow-outline-green sm-text-sm sm-leading-5">Comprar</button>
</span> </span>
<span> <span>
<button x-on:click="verPromo=false" type="button" <button x-on:click="verPromo=false" type="button"
@@ -635,8 +598,7 @@
class="w-10 h-10 float-right bg-[#B221FD] hover:bg-[#7a02b8] rounded-full active:shadow-lg mouse shadow ease-in focus:outline-none"> class="w-10 h-10 float-right bg-[#B221FD] hover:bg-[#7a02b8] rounded-full active:shadow-lg mouse shadow ease-in focus:outline-none">
<svg viewBox="0 0 20 20" enable-background="new 0 0 20 20" <svg viewBox="0 0 20 20" enable-background="new 0 0 20 20"
class="w-6 h-6 inline-block"> class="w-6 h-6 inline-block">
<path fill="#FFFFFF" <path fill="#FFFFFF" d="M16,10c0,0.553-0.048,1-0.601,1H11v4.399C11,15.951,10.553,16,10,16c-0.553,0-1-0.049-1-0.601V11H4.601
d="M16,10c0,0.553-0.048,1-0.601,1H11v4.399C11,15.951,10.553,16,10,16c-0.553,0-1-0.049-1-0.601V11H4.601
C4.049,11,4,10.553,4,10c0-0.553,0.049-1,0.601-1H9V4.601C9,4.048,9.447,4,10,4c0.553,0,1,0.048,1,0.601V9h4.399 C4.049,11,4,10.553,4,10c0-0.553,0.049-1,0.601-1H9V4.601C9,4.048,9.447,4,10,4c0.553,0,1,0.048,1,0.601V9h4.399
C15.952,9,16,9.447,16,10z" /> C15.952,9,16,9.447,16,10z" />
</svg> </svg>
@@ -818,39 +780,48 @@
</p> </p>
</div> </div>
<div class="mb-4"> <div class="mb-4">
<div class="border-2 flex text-gray-400 text-xs text-center justify-center gap-2 py-3 rounded-lg md:mx-3 2xl:mx-6"> <div
El valor de ${{ number_format($total = (int) $precioAhora * (int) $cantidad_comprarPromo) }} será descontado de su saldo. class="border-2 flex text-gray-400 text-xs text-center justify-center gap-2 py-3 rounded-lg md:mx-3 2xl:mx-6">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" class="w-4" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve"> El valor de
${{ number_format($total = (int) $precioAhora * (int) $cantidad_comprarPromo) }} será
descontado de su saldo.
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs"
class="w-4" x="0" y="0" viewBox="0 0 24 24"
style="enable-background:new 0 0 512 512" xml:space="preserve">
<g> <g>
<path d="M12,0A12,12,0,1,0,24,12,12.013,12.013,0,0,0,12,0Zm0,22A10,10,0,1,1,22,12,10.011,10.011,0,0,1,12,22Z" fill="#9f9f9f" data-original="#000000"></path> <path
<path d="M12,5a1,1,0,0,0-1,1v8a1,1,0,0,0,2,0V6A1,1,0,0,0,12,5Z" fill="#9f9f9f" data-original="#000000"></path> d="M12,0A12,12,0,1,0,24,12,12.013,12.013,0,0,0,12,0Zm0,22A10,10,0,1,1,22,12,10.011,10.011,0,0,1,12,22Z"
<rect x="11" y="17" width="2" height="2" rx="1" fill="#9f9f9f" data-original="#000000"></rect> fill="#9f9f9f" data-original="#000000"></path>
<path d="M12,5a1,1,0,0,0-1,1v8a1,1,0,0,0,2,0V6A1,1,0,0,0,12,5Z" fill="#9f9f9f"
data-original="#000000"></path>
<rect x="11" y="17" width="2" height="2" rx="1"
fill="#9f9f9f" data-original="#000000"></rect>
</g> </g>
</svg> </svg>
</div> </div>
@if ($total > auth()->user()->saldo->valor) @if ($total > auth()->user()->saldo->valor)
<div class=" flex text-red-400 text-xs text-center justify-center gap-2 py-2 rounded-lg md:mx-2 2xl:mx-6"> <div
class=" flex text-red-400 text-xs text-center justify-center gap-2 py-2 rounded-lg md:mx-2 2xl:mx-6">
<p>No tiene fondo suficientes para esta compra, recargue su cuenta.</p> <p>No tiene fondo suficientes para esta compra, recargue su cuenta.</p>
@if (auth()->user()->rol->nombre != 'editor' && auth()->user()->rol->nombre != 'administrador') @if (auth()->user()->rol->nombre != 'editor' && auth()->user()->rol->nombre != 'administrador')
<a href="{{ route('recarga') }}"><button class="bg-green-600 text-white rounded px-2 py-1">Recargar</button></a> <a href="{{ route('recarga') }}"><button
class="bg-green-600 text-white rounded px-2 py-1">Recargar</button></a>
@endif @endif
</div> </div>
@endif @endif
</div> </div>
<div class="bg-white px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse sm:justify-evenly gap-5 mt-4"> <div
class="bg-white px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse sm:justify-evenly gap-5 mt-4">
<span> <span>
<!-- Botón de Comprar con protección de múltiples clics y mensaje de procesando --> <!-- Botón de Comprar con protección de múltiples clics y mensaje de procesando -->
<button id="comprarButton" <button id="comprarButton" wire:click="pagar" wire:loading.attr="disabled"
wire:click="pagar"
wire:loading.attr="disabled"
type="button" type="button"
@if ($total <= auth()->user()->saldo->valor) @if ($total <= auth()->user()->saldo->valor) class="inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 sm:mb-[0] mb-2 text-white bg-[#B221FD] hover:bg-[#7a02b8] focus:shadow-outline-green sm-text-sm sm-leading-5"
class="inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 sm:mb-[0] mb-2 text-white bg-[#B221FD] hover:bg-[#7a02b8] focus:shadow-outline-green sm-text-sm sm-leading-5"
@else @else
disabled disabled
class="cursor-no-drop inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 sm:mb-[0] mb-2 text-white bg-gray-200 hover:bg-gray-200 focus:shadow-outline-green sm-text-sm sm-leading-5" class="cursor-no-drop inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 sm:mb-[0] mb-2 text-white bg-gray-200 hover:bg-gray-200 focus:shadow-outline-green sm-text-sm sm-leading-5" @endif
@endif
onclick="enviarInfoCompra()"> onclick="enviarInfoCompra()">
Comprar Comprar
</button> </button>
@@ -892,7 +863,9 @@
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}' 'X-CSRF-TOKEN': '{{ csrf_token() }}'
}, },
body: JSON.stringify({ /* puedes enviar datos si es necesario */ }) body: JSON.stringify({
/* puedes enviar datos si es necesario */
})
}) })
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
@@ -1053,7 +1026,6 @@
@if (!empty($cuenta->servicio->ver_url)) @if (!empty($cuenta->servicio->ver_url))
*URL:* {{ $cuenta->servicio->url }} *URL:* {{ $cuenta->servicio->url }}
@else @else
*Servicio:* {{ $cuenta->servicio->nombre }} @foreach ($cuenta->perfil as $perfil) *Servicio:* {{ $cuenta->servicio->nombre }} @foreach ($cuenta->perfil as $perfil)
@if ($cuentas->id == $perfil->historial_id && $cuenta->estado == 'activo') @if ($cuentas->id == $perfil->historial_id && $cuenta->estado == 'activo')
- Perfil {{ $perfil->perfil }} @if (!empty($cuenta->servicio->perfiles)) - Perfil {{ $perfil->perfil }} @if (!empty($cuenta->servicio->perfiles))
@@ -1119,12 +1091,9 @@
</div> </div>
<div <div class="bg-white px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse sm:justify-evenly gap-5 mt-4">
class="bg-white px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse sm:justify-evenly gap-5 mt-4">
<span> <span>
<button x-on:click="credenciales=false" type="button" <button x-on:click="credenciales=false" type="button" class="inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 text-white bg-[#585858] hover:bg-[#3a3a3a] focus:shadow-outline-green sm-text-sm sm-leading-5">Cerrar</button>
class="inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 text-white bg-[#585858] hover:bg-[#3a3a3a] focus:shadow-outline-green sm-text-sm sm-leading-5">Cerrar</button>
</span> </span>
</div> </div>
@@ -1165,7 +1134,35 @@
</div> </div>
</div> </div>
</div>
<script> <script>
document.addEventListener('livewire:load', () => {
const swiper = new Swiper('.swiper-container', {
slidesPerView: 3,
spaceBetween: 10,
loop: true,
centeredSlides: true,
autoplay: {
delay: 5000,
disableOnInteraction: false,
pauseOnMouseEnter: true,
},
breakpoints: {
640: { slidesPerView: 2, spaceBetween: 20 },
1024: { slidesPerView: 3, spaceBetween: 30 },
1440: { slidesPerView: 4, spaceBetween: 40 },
}
});
Livewire.on('refreshCarousel', () => {
swiper.update();
});
});
function copiarAlPortapapeles2(id_elemento) { function copiarAlPortapapeles2(id_elemento) {
var aux = document.createElement("input"); var aux = document.createElement("input");
aux.setAttribute("value", document.getElementById(id_elemento).innerHTML); aux.setAttribute("value", document.getElementById(id_elemento).innerHTML);
@@ -1175,20 +1172,6 @@
document.body.removeChild(aux); document.body.removeChild(aux);
} }
const prev2 = document.querySelector('.prev2')
const next2 = document.querySelector('.next2')
const slider2 = document.querySelector('.slider2')
prev2.addEventListener('click', () => {
slider2.scrollLeft -= 600
})
next2.addEventListener('click', () => {
slider2.scrollLeft += 600
})
function copyToClipboard2() { function copyToClipboard2() {
var copyText = document.getElementById("d").value; var copyText = document.getElementById("d").value;
navigator.clipboard.writeText(copyText).then(() => { navigator.clipboard.writeText(copyText).then(() => {
@@ -1198,5 +1181,3 @@
}); });
} }
</script> </script>
</div>
@@ -488,10 +488,10 @@
<!-- Rol --> <!-- Rol -->
<div class="mb-4"> <div class="mb-4">
<label for="estado_promocion" class="block text-gray-700 text-sm font-bold mb-2">Selecciona rol:</label> <label for="rol" class="block text-gray-700 text-sm font-bold mb-2">Selecciona rol:</label>
<select id="estado_promocion" wire:model="rol" <select id="rol" wire:model="rol"
class="w-full border-2 border-neutral-200 rounded-[0.8rem] shadow appearance-none ring-0 focus:ring-0 focus:border-neutral-300 py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"> class="w-full border-2 border-neutral-200 rounded-[0.8rem] shadow appearance-none ring-0 focus:ring-0 focus:border-neutral-300 py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
<option selected>Rol</option> <option value="" selected>Rol</option>
@foreach ($roles as $rol) @foreach ($roles as $rol)
<option value="{{ $rol->id }}">{{ $rol->nombre }}</option> <option value="{{ $rol->id }}">{{ $rol->nombre }}</option>
@endforeach @endforeach