Inicialización del módulo base

This commit is contained in:
Arturo Corro 2025-01-27 02:22:06 -06:00
commit 28c3a81b64
1127 changed files with 109613 additions and 0 deletions

28
composer.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "koneko/laravel-vuexy-admin-module",
"description": "Base modular para proyectos Laravel altamente personalizados.",
"type": "library",
"license": "MIT",
"require": {
"php": ">=8.0",
"laravel/framework": "^10.0|^11.0"
},
"autoload": {
"psr-4": {
"Koneko\\BaseModule\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"Koneko\\BaseModule\\BaseServiceProvider"
]
}
},
"authors": [
{
"name": "Arturo Corro Pacheco",
"email": "arturo@koneko.mx"
}
]
}

View File

@ -0,0 +1,40 @@
<?php
namespace Modules\Admin\App\Actions\Fortify;
use Laravel\Fortify\Contracts\CreatesNewUsers;
use Modules\Admin\App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
class CreateNewUser implements CreatesNewUsers
{
use PasswordValidationRules;
/**
* Validate and create a newly registered user.
*
* @param array<string, string> $input
*/
public function create(array $input): User
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique(User::class),
],
'password' => $this->passwordRules(),
])->validate();
return User::create([
'name' => $input['name'],
'email' => $input['email'],
'password' => Hash::make($input['password']),
]);
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace Modules\Admin\App\Actions\Fortify;
use Illuminate\Validation\Rules\Password;
trait PasswordValidationRules
{
/**
* Get the validation rules used to validate passwords.
*
* @return array<int, \Illuminate\Contracts\Validation\Rule|array<mixed>|string>
*/
protected function passwordRules(): array
{
return ['required', 'string', Password::default(), 'confirmed'];
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace Modules\Admin\App\Actions\Fortify;
use Laravel\Fortify\Contracts\ResetsUserPasswords;
use Modules\Admin\App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class ResetUserPassword implements ResetsUserPasswords
{
use PasswordValidationRules;
/**
* Validate and reset the user's forgotten password.
*
* @param array<string, string> $input
*/
public function reset(User $user, array $input): void
{
Validator::make($input, [
'password' => $this->passwordRules(),
])->validate();
$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Modules\Admin\App\Actions\Fortify;
use Laravel\Fortify\Contracts\UpdatesUserPasswords;
use Modules\Admin\App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class UpdateUserPassword implements UpdatesUserPasswords
{
use PasswordValidationRules;
/**
* Validate and update the user's password.
*
* @param array<string, string> $input
*/
public function update(User $user, array $input): void
{
Validator::make($input, [
'current_password' => ['required', 'string', 'current_password:web'],
'password' => $this->passwordRules(),
], [
'current_password.current_password' => __('The provided password does not match your current password.'),
])->validateWithBag('updatePassword');
$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace Modules\Admin\App\Actions\Fortify;
use Laravel\Fortify\Contracts\UpdatesUserProfileInformation;
use Modules\Admin\App\Models\User;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
class UpdateUserProfileInformation implements UpdatesUserProfileInformation
{
/**
* Validate and update the given user's profile information.
*
* @param array<string, string> $input
*/
public function update(User $user, array $input): void
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique('users')->ignore($user->id),
],
])->validateWithBag('updateProfileInformation');
if (
$input['email'] !== $user->email &&
$user instanceof MustVerifyEmail
) {
$this->updateVerifiedUser($user, $input);
} else {
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
])->save();
}
}
/**
* Update the given verified user's profile information.
*
* @param array<string, string> $input
*/
protected function updateVerifiedUser(User $user, array $input): void
{
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
'email_verified_at' => null,
])->save();
$user->sendEmailVerificationNotification();
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace Modules\Admin\App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class CleanInitialAvatars extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'avatars:clean-initial';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Elimina avatares generados automáticamente en el directorio initial-avatars';
/**
* Execute the console command.
*/
public function handle()
{
$directory = 'initial-avatars';
$files = Storage::disk('public')->files($directory);
foreach ($files as $file) {
$lastModified = Storage::disk('public')->lastModified($file);
// Elimina archivos no accedidos en los últimos 30 días
if (now()->timestamp - $lastModified > 30 * 24 * 60 * 60) {
Storage::disk('public')->delete($file);
}
}
$this->info('Avatares iniciales antiguos eliminados.');
}
}

View File

@ -0,0 +1,209 @@
<?php
namespace Modules\Admin\App\Helpers;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Str;
class Helpers
{
public static function appClasses()
{
$data = config('custom.custom');
// default data array
$DefaultData = [
'myLayout' => 'vertical',
'myTheme' => 'theme-default',
'myStyle' => 'light',
'myRTLSupport' => false,
'myRTLMode' => true,
'hasCustomizer' => true,
'showDropdownOnHover' => true,
'displayCustomizer' => true,
'contentLayout' => 'compact',
'headerType' => 'fixed',
'navbarType' => 'fixed',
'menuFixed' => true,
'menuCollapsed' => false,
'footerFixed' => false,
'customizerControls' => [
'rtl',
'style',
'headerType',
'contentLayout',
'layoutCollapsed',
'showDropdownOnHover',
'layoutNavbarOptions',
'themes',
],
// 'defaultLanguage'=>'en',
];
// if any key missing of array from custom.php file it will be merge and set a default value from dataDefault array and store in data variable
$data = array_merge($DefaultData, $data);
// All options available in the template
$allOptions = [
'myLayout' => ['vertical', 'horizontal', 'blank', 'front'],
'menuCollapsed' => [true, false],
'hasCustomizer' => [true, false],
'showDropdownOnHover' => [true, false],
'displayCustomizer' => [true, false],
'contentLayout' => ['compact', 'wide'],
'headerType' => ['fixed', 'static'],
'navbarType' => ['fixed', 'static', 'hidden'],
'myStyle' => ['light', 'dark', 'system'],
'myTheme' => ['theme-default', 'theme-bordered', 'theme-semi-dark'],
'myRTLSupport' => [true, false],
'myRTLMode' => [true, false],
'menuFixed' => [true, false],
'footerFixed' => [true, false],
'customizerControls' => [],
// 'defaultLanguage'=>array('en'=>'en','fr'=>'fr','de'=>'de','ar'=>'ar'),
];
//if myLayout value empty or not match with default options in custom.php config file then set a default value
foreach ($allOptions as $key => $value) {
if (array_key_exists($key, $DefaultData)) {
if (gettype($DefaultData[$key]) === gettype($data[$key])) {
// data key should be string
if (is_string($data[$key])) {
// data key should not be empty
if (isset($data[$key]) && $data[$key] !== null) {
// data key should not be exist inside allOptions array's sub array
if (!array_key_exists($data[$key], $value)) {
// ensure that passed value should be match with any of allOptions array value
$result = array_search($data[$key], $value, 'strict');
if (empty($result) && $result !== 0) {
$data[$key] = $DefaultData[$key];
}
}
} else {
// if data key not set or
$data[$key] = $DefaultData[$key];
}
}
} else {
$data[$key] = $DefaultData[$key];
}
}
}
$styleVal = $data['myStyle'] == "dark" ? "dark" : "light";
$styleUpdatedVal = $data['myStyle'] == "dark" ? "dark" : $data['myStyle'];
// Determine if the layout is admin or front based on cookies
$layoutName = $data['myLayout'];
$isAdmin = Str::contains($layoutName, 'front') ? false : true;
$modeCookieName = $isAdmin ? 'admin-mode' : 'front-mode';
$colorPrefCookieName = $isAdmin ? 'admin-colorPref' : 'front-colorPref';
// Determine style based on cookies, only if not 'blank-layout'
if ($layoutName !== 'blank') {
if (isset($_COOKIE[$modeCookieName])) {
$styleVal = $_COOKIE[$modeCookieName];
if ($styleVal === 'system') {
$styleVal = isset($_COOKIE[$colorPrefCookieName]) ? $_COOKIE[$colorPrefCookieName] : 'light';
}
$styleUpdatedVal = $_COOKIE[$modeCookieName];
}
}
isset($_COOKIE['theme']) ? $themeVal = $_COOKIE['theme'] : $themeVal = $data['myTheme'];
$directionVal = isset($_COOKIE['direction']) ? ($_COOKIE['direction'] === "true" ? 'rtl' : 'ltr') : $data['myRTLMode'];
//layout classes
$layoutClasses = [
'layout' => $data['myLayout'],
'theme' => $themeVal,
'themeOpt' => $data['myTheme'],
'style' => $styleVal,
'styleOpt' => $data['myStyle'],
'styleOptVal' => $styleUpdatedVal,
'rtlSupport' => $data['myRTLSupport'],
'rtlMode' => $data['myRTLMode'],
'textDirection' => $directionVal, //$data['myRTLMode'],
'menuCollapsed' => $data['menuCollapsed'],
'hasCustomizer' => $data['hasCustomizer'],
'showDropdownOnHover' => $data['showDropdownOnHover'],
'displayCustomizer' => $data['displayCustomizer'],
'contentLayout' => $data['contentLayout'],
'headerType' => $data['headerType'],
'navbarType' => $data['navbarType'],
'menuFixed' => $data['menuFixed'],
'footerFixed' => $data['footerFixed'],
'customizerControls' => $data['customizerControls'],
];
// sidebar Collapsed
if ($layoutClasses['menuCollapsed'] == true) {
$layoutClasses['menuCollapsed'] = 'layout-menu-collapsed';
}
// Header Type
if ($layoutClasses['headerType'] == 'fixed') {
$layoutClasses['headerType'] = 'layout-menu-fixed';
}
// Navbar Type
if ($layoutClasses['navbarType'] == 'fixed') {
$layoutClasses['navbarType'] = 'layout-navbar-fixed';
} elseif ($layoutClasses['navbarType'] == 'static') {
$layoutClasses['navbarType'] = '';
} else {
$layoutClasses['navbarType'] = 'layout-navbar-hidden';
}
// Menu Fixed
if ($layoutClasses['menuFixed'] == true) {
$layoutClasses['menuFixed'] = 'layout-menu-fixed';
}
// Footer Fixed
if ($layoutClasses['footerFixed'] == true) {
$layoutClasses['footerFixed'] = 'layout-footer-fixed';
}
// RTL Supported template
if ($layoutClasses['rtlSupport'] == true) {
$layoutClasses['rtlSupport'] = '/rtl';
}
// RTL Layout/Mode
if ($layoutClasses['rtlMode'] == true) {
$layoutClasses['rtlMode'] = 'rtl';
$layoutClasses['textDirection'] = isset($_COOKIE['direction']) ? ($_COOKIE['direction'] === "true" ? 'rtl' : 'ltr') : 'rtl';
} else {
$layoutClasses['rtlMode'] = 'ltr';
$layoutClasses['textDirection'] = isset($_COOKIE['direction']) && $_COOKIE['direction'] === "true" ? 'rtl' : 'ltr';
}
// Show DropdownOnHover for Horizontal Menu
if ($layoutClasses['showDropdownOnHover'] == true) {
$layoutClasses['showDropdownOnHover'] = true;
} else {
$layoutClasses['showDropdownOnHover'] = false;
}
// To hide/show display customizer UI, not js
if ($layoutClasses['displayCustomizer'] == true) {
$layoutClasses['displayCustomizer'] = true;
} else {
$layoutClasses['displayCustomizer'] = false;
}
return $layoutClasses;
}
public static function updatePageConfig($pageConfigs)
{
$demo = 'custom';
if (isset($pageConfigs)) {
if (count($pageConfigs) > 0) {
foreach ($pageConfigs as $config => $val) {
Config::set('custom.' . $demo . '.' . $config, $val);
}
}
}
}
}

View File

@ -0,0 +1,62 @@
<?php
namespace Modules\Admin\App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Modules\Admin\App\Models\Setting;
use Modules\Admin\App\Services\VuexyAdminService;
class AdminController extends Controller
{
public function searchNavbar()
{
abort_if(!request()->expectsJson(), 403, __('errors.ajax_only'));
$VuexyAdminService = app(VuexyAdminService::class);
return response()->json($VuexyAdminService->getVuexySearchData());
}
public function quickLinksUpdate(Request $request)
{
abort_if(!request()->expectsJson(), 403, __('errors.ajax_only'));
$validated = $request->validate([
'action' => 'required|in:update,remove',
'route' => 'required|string',
]);
$quickLinks = Setting::where('user_id', Auth::user()->id)
->where('key', 'quicklinks')
->first();
$quickLinks = $quickLinks ? json_decode($quickLinks->value, true) : [];
if ($validated['action'] === 'update') {
// Verificar si ya existe
if (!in_array($validated['route'], $quickLinks))
$quickLinks[] = $validated['route'];
} elseif ($validated['action'] === 'remove') {
// Eliminar la ruta si existe
$quickLinks = array_filter($quickLinks, function ($route) use ($validated) {
return $route !== $validated['route'];
});
}
Setting::updateOrCreate(['user_id' => Auth::user()->id, 'key' => 'quicklinks'], ['value' => json_encode($quickLinks)]);
VuexyAdminService::clearQuickLinksCache();
}
public function generalSettings()
{
return view('admin::admin-settings.webapp-general-settings');
}
public function smtpSettings()
{
return view('admin::admin-settings.smtp-settings');
}
}

View File

@ -0,0 +1,144 @@
<?php
namespace Modules\Admin\App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Laravel\Fortify\Features;
class AuthController extends Controller
{
/*
public function loginView()
{
dd($viewMode);
$viewMode = config('custom.custom.authViewMode');
$pageConfigs = ['myLayout' => 'blank'];
return view("admin::auth.login-{$viewMode}", ['pageConfigs' => $pageConfigs]);
}
public function registerView()
{
if (!Features::enabled(Features::registration()))
abort(403, 'El registro está deshabilitado.');
$viewMode = config('custom.custom.authViewMode');
$pageConfigs = ['myLayout' => 'blank'];
return view("admin::auth.register-{$viewMode}", ['pageConfigs' => $pageConfigs]);
}
public function confirmPasswordView()
{
if (!Features::enabled(Features::registration()))
abort(403, 'El registro está deshabilitado.');
$viewMode = config('custom.custom.authViewMode');
$pageConfigs = ['myLayout' => 'blank'];
return view("admin::auth.confirm-password-{$viewMode}", ['pageConfigs' => $pageConfigs]);
}
public function resetPasswordView()
{
if (!Features::enabled(Features::resetPasswords()))
abort(403, 'El registro está deshabilitado.');
$viewMode = config('custom.custom.authViewMode');
$pageConfigs = ['myLayout' => 'blank'];
return view("admin::auth.reset-password-{$viewMode}", ['pageConfigs' => $pageConfigs]);
}
public function requestPasswordResetLinkView(Request $request)
{
if (!Features::enabled(Features::resetPasswords()))
abort(403, 'El registro está deshabilitado.');
$viewMode = config('custom.custom.authViewMode');
$pageConfigs = ['myLayout' => 'blank'];
return view("admin::auth.reset-password-{$viewMode}", ['pageConfigs' => $pageConfigs, 'request' => $request]);
}
public function twoFactorChallengeView()
{
if (!Features::enabled(Features::registration()))
abort(403, 'El registro está deshabilitado.');
$viewMode = config('custom.custom.authViewMode');
$pageConfigs = ['myLayout' => 'blank'];
return view("admin::auth.two-factor-challenge-{$viewMode}", ['pageConfigs' => $pageConfigs]);
}
public function twoFactorRecoveryCodesView()
{
if (!Features::enabled(Features::registration()))
abort(403, 'El registro está deshabilitado.');
$viewMode = config('custom.custom.authViewMode');
$pageConfigs = ['myLayout' => 'blank'];
return view("admin::auth.register-{$viewMode}", ['pageConfigs' => $pageConfigs]);
}
public function twoFactorAuthenticationView()
{
if (!Features::enabled(Features::registration()))
abort(403, 'El registro está deshabilitado.');
$viewMode = config('custom.custom.authViewMode');
$pageConfigs = ['myLayout' => 'blank'];
return view("admin::auth.register-{$viewMode}", ['pageConfigs' => $pageConfigs]);
}
public function verifyEmailView()
{
if (!Features::enabled(Features::registration()))
abort(403, 'El registro está deshabilitado.');
$viewMode = config('custom.custom.authViewMode');
$pageConfigs = ['myLayout' => 'blank'];
return view("admin::auth.verify-email-{$viewMode}", ['pageConfigs' => $pageConfigs]);
}
public function showEmailVerificationForm()
{
if (!Features::enabled(Features::registration()))
abort(403, 'El registro está deshabilitado.');
$viewMode = config('custom.custom.authViewMode');
$pageConfigs = ['myLayout' => 'blank'];
return view("admin::auth.register-{$viewMode}", ['pageConfigs' => $pageConfigs]);
}
public function userProfileView()
{
if (!Features::enabled(Features::registration()))
abort(403, 'El registro está deshabilitado.');
$viewMode = config('custom.custom.authViewMode');
$pageConfigs = ['myLayout' => 'blank'];
return view("admin::auth.register-{$viewMode}", ['pageConfigs' => $pageConfigs]);
}
*/
}

View File

@ -0,0 +1,41 @@
<?php
namespace Modules\Admin\App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Artisan;
use Modules\Admin\App\Services\CacheConfigService;
class CacheController extends Controller
{
public function generateConfigCache()
{
try {
// Lógica para generar cache
Artisan::call('config:cache');
return response()->json(['success' => true, 'message' => 'Cache generado correctamente.']);
} catch (\Exception $e) {
return response()->json(['success' => false, 'message' => 'Error al generar el cache.', 'error' => $e->getMessage()], 500);
}
}
public function generateRouteCache()
{
try {
// Lógica para generar cache de rutas
Artisan::call('route:cache');
return response()->json(['success' => true, 'message' => 'Cache de rutas generado correctamente.']);
} catch (\Exception $e) {
return response()->json(['success' => false, 'message' => 'Error al generar el cache de rutas.', 'error' => $e->getMessage()], 500);
}
}
public function cacheManager(CacheConfigService $cacheConfigService)
{
$configCache = $cacheConfigService->getConfig();
return view('admin::cache-manager.index', compact('configCache'));
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Modules\Admin\App\Http\Controllers;
use App\Http\Controllers\Controller;
class HomeController extends Controller
{
public function index()
{
return view('admin::pages.home');
}
public function about()
{
return view('admin::pages.about');
}
public function comingsoon()
{
$pageConfigs = ['myLayout' => 'blank'];
return view('admin::pages.comingsoon', compact('pageConfigs'));
}
public function underMaintenance()
{
$pageConfigs = ['myLayout' => 'blank'];
return view('admin::pages.under-maintenance', compact('pageConfigs'));
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace Modules\Admin\App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Spatie\Permission\Models\Permission;
use Yajra\DataTables\Facades\DataTables;
use App\Http\Controllers\Controller;
class PermissionController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
if ($request->ajax()) {
$permissions = Permission::latest()->get();
return DataTables::of($permissions)
->addIndexColumn()
->addColumn('assigned_to', function ($row) {
return (Arr::pluck($row->roles, ['name']));
})
->editColumn('created_at', function ($request) {
return $request->created_at->format('Y-m-d h:i:s a');
})
->make(true);
}
return view('admin::permissions.index');
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace Modules\Admin\App\Http\Controllers;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Role;
use App\Http\Controllers\Controller;
class RoleController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
return view('admin::roles.index');
}
public function checkUniqueRoleName(Request $request)
{
$id = $request->input('id');
$name = $request->input('name');
// Verificar si el nombre ya existe en la base de datos
$existingRole = Role::where('name', $name)
->whereNot('id', $id)
->first();
if ($existingRole) {
return response()->json(['valid' => false]);
}
return response()->json(['valid' => true]);
}
}

View File

@ -0,0 +1,194 @@
<?php
namespace Modules\Admin\App\Http\Controllers;
use Modules\Admin\App\Imports\SatCatalogsImport;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use App\Http\Controllers\Controller;
class SatCatalogController extends Controller
{
/**
* @return \Illuminate\Support\Collection
*/
public function import(Request $request)
{
// Si es una solicitud POST, procesa la importación
if ($request->isMethod('post')) {
$validator = Validator::make($request->all(), [
'file' => ['required', 'mimes:xls,xlsx'],
]);
if ($validator->fails())
return back()->withInput()->withErrors($validator);
$time_start = microtime(true);
$results = [
'details' => [],
'total_rows' => 0,
'total_created' => 0,
'total_updated' => 0,
'total_time' => 0,
];
$import = new SatCatalogsImport();
$filePath = request()->file('file');
$time_sheet_start = microtime(true);
$import->onlySheets('c_FormaPago');
Excel::import($import, $filePath);
$results['details'][] = [
'name' => 'Forma de pago',
'time' => round(microtime(true) - $time_sheet_start, 2),
'created' => session('created'),
'updated' => session('updated'),
];
$results['total_rows'] += session('created') + session('updated');
$results['total_created'] += session('created');
$results['total_updated'] += session('updated');
$time_sheet_start = microtime(true);
$import->onlySheets('c_Moneda');
Excel::import($import, $filePath);
$results['details'][] = [
'name' => 'Moneda',
'time' => round(microtime(true) - $time_sheet_start, 2),
'created' => session('created'),
'updated' => session('updated'),
];
$results['total_rows'] += session('created') + session('updated');
$results['total_created'] += session('created');
$results['total_updated'] += session('updated');
$time_sheet_start = microtime(true);
$import->onlySheets('c_CodigoPostal_Parte_1');
Excel::import($import, $filePath);
$import->onlySheets('c_CodigoPostal_Parte_2');
Excel::import($import, $filePath);
$results['details'][] = [
'name' => 'Código postal',
'time' => round(microtime(true) - $time_sheet_start, 2),
'created' => session('created'),
'updated' => session('updated'),
];
$time_sheet_start = microtime(true);
$import->onlySheets('c_RegimenFiscal');
Excel::import($import, $filePath);
$results['details'][] = [
'name' => 'Regimen fiscal',
'time' => round(microtime(true) - $time_sheet_start, 2),
'created' => session('created'),
'updated' => session('updated'),
];
$results['total_rows'] += session('created') + session('updated');
$results['total_created'] += session('created');
$results['total_updated'] += session('updated');
$time_sheet_start = microtime(true);
$import->onlySheets('c_Pais');
Excel::import($import, $filePath);
$results['details'][] = [
'name' => 'País',
'time' => round(microtime(true) - $time_sheet_start, 2),
'created' => session('created'),
'updated' => session('updated'),
];
$results['total_rows'] += session('created') + session('updated');
$results['total_created'] += session('created');
$results['total_updated'] += session('updated');
$time_sheet_start = microtime(true);
$import->onlySheets('c_UsoCFDI');
Excel::import($import, $filePath);
$results['details'][] = [
'name' => 'Uso de CFDI',
'time' => round(microtime(true) - $time_sheet_start, 2),
'created' => session('created'),
'updated' => session('updated'),
];
$results['total_rows'] += session('created') + session('updated');
$results['total_created'] += session('created');
$results['total_updated'] += session('updated');
$time_sheet_start = microtime(true);
$import->onlySheets('C_Colonia_1');
Excel::import($import, $filePath);
$time_sheet_start = microtime(true);
$import->onlySheets('C_Colonia_2');
Excel::import($import, $filePath);
$time_sheet_start = microtime(true);
$import->onlySheets('C_Colonia_3');
Excel::import($import, $filePath);
$results['details'][] = [
'name' => 'Colonia',
'time' => round(microtime(true) - $time_sheet_start, 2),
'created' => session('created'),
'updated' => session('updated'),
];
$results['total_rows'] += session('created') + session('updated');
$results['total_created'] += session('created');
$results['total_updated'] += session('updated');
$time_sheet_start = microtime(true);
$import->onlySheets('c_Estado');
Excel::import($import, $filePath);
$results['details'][] = [
'name' => 'Estado',
'time' => round(microtime(true) - $time_sheet_start, 2),
'created' => session('created'),
'updated' => session('updated'),
];
$results['total_rows'] += session('created') + session('updated');
$results['total_created'] += session('created');
$results['total_updated'] += session('updated');
$time_sheet_start = microtime(true);
$import->onlySheets('C_Localidad');
Excel::import($import, $filePath);
$results['details'][] = [
'name' => 'Localidad',
'time' => round(microtime(true) - $time_sheet_start, 2),
'created' => session('created'),
'updated' => session('updated'),
];
$results['total_rows'] += session('created') + session('updated');
$results['total_created'] += session('created');
$results['total_updated'] += session('updated');
$time_sheet_start = microtime(true);
$import->onlySheets('C_Municipio');
Excel::import($import, $filePath);
$results['details'][] = [
'name' => 'Municipio',
'time' => round(microtime(true) - $time_sheet_start, 2),
'created' => session('created'),
'updated' => session('updated'),
];
$results['total_rows'] += session('created') + session('updated');
$results['total_created'] += session('created');
$results['total_updated'] += session('updated');
$results['total_time'] = round(microtime(true) - $time_start, 2);
return back()->with('results', $results);
}
return view('system::import-sat.index');
}
}

View File

@ -0,0 +1,182 @@
<?php
namespace Modules\Admin\App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth;
use Yajra\DataTables\Facades\DataTables;
use Modules\Admin\App\Models\User;
class UserController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
if ($request->ajax()) {
$users = User::when(!Auth::user()->hasRole('SuperAdmin'), function ($query) {
$query->where('id', '>', 1);
})
->latest()
->get();
return DataTables::of($users)
->only(['id', 'name', 'email', 'avatar', 'roles', 'status', 'created_at'])
->addIndexColumn()
->addColumn('avatar', function ($user) {
return $user->profile_photo_url;
})
->addColumn('roles', function ($user) {
return (Arr::pluck($user->roles, ['name']));
})
/*
->addColumn('stores', function ($user) {
return (Arr::pluck($user->stores, ['nombre']));
})
y*/
->editColumn('created_at', function ($user) {
return $user->created_at->format('Y-m-d');
})
->make(true);
}
return view('admin::users.index');
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|max:255',
'email' => 'required|max:255|unique:users',
'photo' => 'nullable|mimes:jpg,jpeg,png|max:1024',
'password' => 'required',
]);
if ($validator->fails())
return response()->json(['errors' => $validator->errors()->all()]);
// Preparamos los datos
$user_request = array_merge_recursive($request->all(), [
'remember_token' => Str::random(10),
'created_by' => Auth::user()->id,
]);
$user_request['password'] = bcrypt($request->password);
// Guardamos el nuevo usuario
$user = User::create($user_request);
// Asignmos los permisos
$user->assignRole($request->roles);
// Asignamos Tiendas
//$user->stores()->attach($request->stores);
if ($request->file('photo'))
$user->updateProfilePhoto($request->file('photo'));
return response()->json(['success' => 'Se agrego correctamente el usuario']);
}
/**
* Display the specified resource.
*
* @param int User $user
* @return \Illuminate\Http\Response
*/
public function show(User $user)
{
return view('admin::users.show', compact('user'));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int User $user
* @return \Illuminate\Http\Response
*/
public function updateAjax(Request $request, User $user)
{
// Validamos los datos
$validator = Validator::make($request->all(), [
'name' => 'required|max:191',
'email' => "required|max:191|unique:users,email," . $user->id,
'photo' => 'nullable|mimes:jpg,jpeg,png|max:2048'
]);
if ($validator->fails())
return response()->json(['errors' => $validator->errors()->all()]);
// Preparamos los datos
$user_request = $request->all();
if ($request->password) {
$user_request['password'] = bcrypt($request->password);
} else {
unset($user_request['password']);
}
// Guardamos los cambios
$user->update($user_request);
// Sincronizamos Roles
$user->syncRoles($request->roles);
// Sincronizamos Tiendas
//$user->stores()->sync($request->stores);
// Actualizamos foto de perfil
if ($request->file('photo'))
$user->updateProfilePhoto($request->file('photo'));
return response()->json(['success' => 'Se guardo correctamente los cambios.']);
}
public function userSettings(User $user)
{
return view('admin::users.user-settings', compact('user'));
}
public function generateAvatar(Request $request)
{
// Validación de entrada
$request->validate([
'name' => 'nullable|string',
'color' => 'nullable|string|size:6',
'background' => 'nullable|string|size:6',
'size' => 'nullable|integer|min:20|max:1024'
]);
$name = $request->get('name', 'NA');
$color = $request->get('color', '7F9CF5');
$background = $request->get('background', 'EBF4FF');
$size = $request->get('size', 100);
return User::getAvatarImage($name, $color, $background, $size);
try {
} catch (\Exception $e) {
// String base64 de una imagen PNG transparente de 1x1 píxel
$transparentBase64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==';
return response()->make(base64_decode($transparentBase64), 200, [
'Content-Type' => 'image/png'
]);
}
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace Modules\Admin\App\Http\Controllers;
use App\Http\Controllers\Controller;
class WebsiteSettingController extends Controller
{
public function generalSettings()
{
return view('admin::website-settings.general-settings-index');
}
public function legal()
{
return view('admin::website-settings.legal-index');
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Modules\Admin\App\Http\Middleware;
use Closure;
use Modules\Admin\App\Services\AdminTemplateService;
use Illuminate\Support\Facades\View;
class AdminTemplateMiddleware
{
public function __construct()
{
//
}
public function handle($request, Closure $next)
{
// Verificar si estamos en el contexto de website
if (!$request->is('admin/*') && !$request->is('admin')) {
return $next($request);
}
// Compartir variables globalmente solo si es una solicitud HTML
if (str_contains($request->header('Accept'), 'text/html')) {
$adminVars = app(AdminTemplateService::class)->getAdminVars();
View::share('_admin', $adminVars);
}
return $next($request);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Modules\Admin\App\Http\View\Composers;
use Illuminate\View\View;
use Modules\Admin\App\Services\VuexyAdminService;
class VuexyTemplateComposer
{
protected $vuexyAdminService;
public function __construct(VuexyAdminService $vuexyAdminService)
{
$this->vuexyAdminService = $vuexyAdminService;
}
public function compose(View $view)
{
$view->with([
'vuexyMenu' => $this->vuexyAdminService->getMenu(),
'vuexySearch' => $this->vuexyAdminService->getSearch(),
'vuexyQuickLinks' => $this->vuexyAdminService->getQuickLinks(),
'vuexyNotifications' => $this->vuexyAdminService->getNotifications(),
'vuexyBreadcrumbs' => $this->vuexyAdminService->getBreadcrumbs(),
]);
}
}

View File

@ -0,0 +1,78 @@
<?php
namespace Modules\Admin\App\Imports;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Concerns\ToCollection;
class SATCodigoPostalImport implements ToCollection
{
public function collection(Collection $collection)
{
$batchSize = 1000;
$batchData = [];
$processedRows = 0;
foreach ($collection as $key => $row) {
if ($key < 7 || !$row[1]) {
continue;
}
$requestArray = [
'c_codigo_postal' => $row[0],
'c_estado' => $row[1],
'c_municipio' => $row[2],
'c_localidad' => $row[3],
'estimulo_franja_fronteriza' => $row[4],
'fecha_inicio_de_vigencia' => $row[5] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[5])) :
null,
'fecha_fin_de_vigencia' => $row[6] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[6])) :
null,
];
$batchData[] = $requestArray;
$processedRows++;
if (count($batchData) >= $batchSize) {
$this->insertBatch($batchData);
$batchData = [];
}
}
if (!empty($batchData)) {
$this->insertBatch($batchData);
}
echo "\n\033[32mImport completed: Processed $processedRows rows.\033[0m\n";
}
private function insertBatch(array $batchData)
{
try {
DB::table('sat_codigo_postal')->upsert(
$batchData,
[
'c_codigo_postal'
],
[
'c_estado',
'c_municipio',
'c_localidad',
'estimulo_franja_fronteriza',
'fecha_inicio_de_vigencia',
'fecha_fin_de_vigencia'
]
);
} catch (\Exception $e) {
echo "Error in batch: " . $e->getMessage() . "\n";
foreach ($batchData as $row) {
echo "Row data: " . json_encode($row) . "\n";
}
}
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace Modules\Admin\App\Imports;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Concerns\ToCollection;
class SATColoniaImport implements ToCollection
{
/**
* @param Collection $collection
*/
public function collection(Collection $collection)
{
$batchSize = 1000;
$batchData = [];
$processedRows = 0;
foreach ($collection as $key => $row) {
if ($key < 5 || !$row[1]) {
continue;
}
$requestArray = [
'c_colonia' => $row[0],
'c_codigo_postal' => $row[1],
'nombre_del_asentamiento' => $row[2],
];
$batchData[] = $requestArray;
$processedRows++;
if (count($batchData) >= $batchSize) {
$this->insertBatch($batchData);
$batchData = [];
}
}
if (!empty($batchData)) {
$this->insertBatch($batchData);
}
echo "\n\033[32mImport completed: Processed $processedRows rows.\033[0m\n";
}
private function insertBatch(array $batchData)
{
try {
DB::table('sat_colonia')->upsert(
$batchData,
[
'c_colonia',
'c_codigo_postal',
],
[
'nombre_del_asentamiento',
]
);
} catch (\Exception $e) {
echo "Error in batch: " . $e->getMessage() . "\n";
foreach ($batchData as $row) {
echo "Row data: " . json_encode($row) . "\n";
}
}
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace Modules\Admin\App\Imports;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Concerns\ToCollection;
class SATEstadoImport implements ToCollection
{
/**
* @param Collection $collection
*/
public function collection(Collection $collection)
{
$batchSize = 1000;
$batchData = [];
$processedRows = 0;
foreach ($collection as $key => $row) {
if ($key < 5 || !$row[1]) {
continue;
}
$requestArray = [
'c_estado' => $row[0],
'c_pais' => $row[1],
'nombre_del_estado' => $row[2],
'fecha_inicio_de_vigencia' => $row[3] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[3])) :
null,
'fecha_fin_de_vigencia' => $row[4] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[4])) :
null,
];
$batchData[] = $requestArray;
$processedRows++;
if (count($batchData) >= $batchSize) {
$this->insertBatch($batchData);
$batchData = [];
}
}
if (!empty($batchData)) {
$this->insertBatch($batchData);
}
echo "\n\033[32mImport completed: Processed $processedRows rows.\033[0m\n";
}
private function insertBatch(array $batchData)
{
try {
DB::table('sat_estado')->upsert(
$batchData,
[
'c_estado',
'c_pais',
],
[
'nombre_del_estado',
'fecha_inicio_de_vigencia',
'fecha_fin_de_vigencia',
]
);
} catch (\Exception $e) {
echo "Error in batch: " . $e->getMessage() . "\n";
foreach ($batchData as $row) {
echo "Row data: " . json_encode($row) . "\n";
}
}
}
}

View File

@ -0,0 +1,95 @@
<?php
namespace Modules\Admin\App\Imports;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Concerns\ToCollection;
class SATFormaPagoImport implements ToCollection
{
/**
* @param Collection $collection
*/
public function collection(Collection $collection)
{
$batchSize = 1000;
$batchData = [];
$processedRows = 0;
foreach ($collection as $key => $row) {
if ($key < 6 || !$row[1]) {
continue;
}
$requestArray = [
'c_forma_pago' => $row[0],
'descripcion' => $row[1],
'bancarizado' => $row[2],
'numero_de_operacion' => $row[3],
'rfc_del_emisor_de_la_cuenta_ordenante' => $row[4],
'cuenta_ordenante' => $row[5],
'patron_para_cuenta_ordenante' => $row[6],
'rfc_del_emisor_cuenta_de_beneficiario' => $row[7],
'cuenta_de_benenficiario' => $row[8],
'patron_para_cuenta_beneficiaria' => $row[9],
'tipo_cadena_pago' => $row[10],
'banco_emisor_de_la_cuenta_ordenante' => $row[11],
'fecha_inicio_de_vigencia' => $row[12] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[12])) :
null,
'fecha_fin_de_vigencia' => $row[13] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[13])) :
null,
];
$batchData[] = $requestArray;
$processedRows++;
if (count($batchData) >= $batchSize) {
$this->insertBatch($batchData);
$batchData = [];
}
}
if (!empty($batchData)) {
$this->insertBatch($batchData);
}
echo "\n\033[32mImport completed: Processed $processedRows rows.\033[0m\n";
}
private function insertBatch(array $batchData)
{
try {
DB::table('sat_forma_pago')->upsert(
$batchData,
[
'c_forma_pago',
],
[
'descripcion',
'bancarizado',
'numero_de_operacion',
'rfc_del_emisor_de_la_cuenta_ordenante',
'cuenta_ordenante',
'patron_para_cuenta_ordenante',
'rfc_del_emisor_cuenta_de_beneficiario',
'cuenta_de_benenficiario',
'patron_para_cuenta_beneficiaria',
'tipo_cadena_pago',
'banco_emisor_de_la_cuenta_ordenante',
'fecha_inicio_de_vigencia',
'fecha_fin_de_vigencia',
]
);
} catch (\Exception $e) {
echo "Error in batch: " . $e->getMessage() . "\n";
foreach ($batchData as $row) {
echo "Row data: " . json_encode($row) . "\n";
}
}
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace Modules\Admin\App\Imports;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Concerns\ToCollection;
class SATLocalidadImport implements ToCollection
{
/**
* @param Collection $collection
*/
public function collection(Collection $collection)
{
$batchSize = 1000;
$batchData = [];
$processedRows = 0;
foreach ($collection as $key => $row) {
if ($key < 5 || !$row[1]) {
continue;
}
$requestArray = [
'c_localidad' => $row[0],
'c_estado' => $row[1],
'descripcion' => $row[2],
'fecha_de_inicio_de_vigencia' => $row[3] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[3])) :
null,
'fecha_de_fin_de_vigencia' => $row[4] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[4])) :
null,
];
$batchData[] = $requestArray;
$processedRows++;
if (count($batchData) >= $batchSize) {
$this->insertBatch($batchData);
$batchData = [];
}
}
if (!empty($batchData)) {
$this->insertBatch($batchData);
}
echo "\n\033[32mImport completed: Processed $processedRows rows.\033[0m\n";
}
private function insertBatch(array $batchData)
{
try {
DB::table('sat_localidad')->upsert(
$batchData,
[
'c_localidad',
'c_estado',
],
[
'descripcion',
'fecha_de_inicio_de_vigencia',
'fecha_de_fin_de_vigencia'
]
);
} catch (\Exception $e) {
echo "Error in batch: " . $e->getMessage() . "\n";
foreach ($batchData as $row) {
echo "Row data: " . json_encode($row) . "\n";
}
}
}
}

View File

@ -0,0 +1,79 @@
<?php
namespace Modules\Admin\App\Imports;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Concerns\ToCollection;
class SATMonedaImport implements ToCollection
{
/**
* @param Collection $collection
*/
public function collection(Collection $collection)
{
$batchSize = 1000;
$batchData = [];
$processedRows = 0;
foreach ($collection as $key => $row) {
if ($key < 5 || !$row[1]) {
continue;
}
$requestArray = [
'c_moneda' => $row[0],
'descripcion' => $row[1],
'decimales' => $row[2],
'porcentaje_variacion' => $row[3],
'fecha_inicio_de_vigencia' => $row[4] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[4])) :
null,
'fecha_fin_de_vigencia' => $row[5] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[5])) :
null,
];
$batchData[] = $requestArray;
$processedRows++;
if (count($batchData) >= $batchSize) {
$this->insertBatch($batchData);
$batchData = [];
}
}
if (!empty($batchData)) {
$this->insertBatch($batchData);
}
echo "\n\033[32mImport completed: Processed $processedRows rows.\033[0m\n";
}
private function insertBatch(array $batchData)
{
try {
DB::table('sat_moneda')->upsert(
$batchData,
[
'c_moneda',
],
[
'descripcion',
'decimales',
'porcentaje_variacion',
'fecha_inicio_de_vigencia',
'fecha_fin_de_vigencia',
]
);
} catch (\Exception $e) {
echo "Error in batch: " . $e->getMessage() . "\n";
foreach ($batchData as $row) {
echo "Row data: " . json_encode($row) . "\n";
}
}
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace Modules\Admin\App\Imports;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Concerns\ToCollection;
class SATMunicipioImport implements ToCollection
{
/**
* @param Collection $collection
*/
public function collection(Collection $collection)
{
$batchSize = 1000;
$batchData = [];
$processedRows = 0;
foreach ($collection as $key => $row) {
if ($key < 5 || !$row[1]) {
continue;
}
$requestArray = [
'c_municipio' => $row[0],
'c_estado' => $row[1],
'descripcion' => $row[2],
'fecha_de_inicio_de_vigencia' => $row[3] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[3])) :
null,
'fecha_de_fin_de_vigencia' => $row[4] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[4])) :
null,
];
$batchData[] = $requestArray;
$processedRows++;
if (count($batchData) >= $batchSize) {
$this->insertBatch($batchData);
$batchData = [];
}
}
if (!empty($batchData)) {
$this->insertBatch($batchData);
}
echo "\n\033[32mImport completed: Processed $processedRows rows.\033[0m\n";
}
private function insertBatch(array $batchData)
{
try {
DB::table('sat_municipio')->upsert(
$batchData,
[
'c_municipio',
'c_estado',
],
[
'descripcion',
'fecha_de_inicio_de_vigencia',
'fecha_de_fin_de_vigencia'
]
);
} catch (\Exception $e) {
echo "Error in batch: " . $e->getMessage() . "\n";
foreach ($batchData as $row) {
echo "Row data: " . json_encode($row) . "\n";
}
}
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace Modules\Admin\App\Imports;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Concerns\ToCollection;
class SATPaisImport implements ToCollection
{
/**
* @param Collection $collection
*/
public function collection(Collection $collection)
{
$batchSize = 1000;
$batchData = [];
$processedRows = 0;
foreach ($collection as $key => $row) {
if ($key < 5 || !$row[1]) {
continue;
}
$requestArray = [
'c_pais' => $row[0],
'descripcion' => $row[1],
'formato_de_codigo_postal' => $row[2],
'formato_de_registro_de_identidad_tributaria' => $row[3],
'validacion_del_registro_de_identidad_tributaria' => $row[4],
'agrupaciones' => $row[5],
];
$batchData[] = $requestArray;
$processedRows++;
if (count($batchData) >= $batchSize) {
$this->insertBatch($batchData);
$batchData = [];
}
}
if (!empty($batchData)) {
$this->insertBatch($batchData);
}
echo "\n\033[32mImport completed: Processed $processedRows rows.\033[0m\n";
}
private function insertBatch(array $batchData)
{
try {
DB::table('sat_pais')->upsert(
$batchData,
[
'c_pais',
],
[
'descripcion',
'formato_de_codigo_postal',
'formato_de_registro_de_identidad_tributaria',
'validacion_del_registro_de_identidad_tributaria',
'agrupaciones',
]
);
} catch (\Exception $e) {
echo "Error in batch: " . $e->getMessage() . "\n";
foreach ($batchData as $row) {
echo "Row data: " . json_encode($row) . "\n";
}
}
}
}

View File

@ -0,0 +1,79 @@
<?php
namespace Modules\Admin\App\Imports;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Concerns\ToCollection;
class SATRegimenFiscalImport implements ToCollection
{
/**
* @param Collection $collection
*/
public function collection(Collection $collection)
{
$batchSize = 1000;
$batchData = [];
$processedRows = 0;
foreach ($collection as $key => $row) {
if ($key < 6 || !$row[1]) {
continue;
}
$requestArray = [
'c_regimen_fiscal' => $row[0],
'descripcion' => $row[1],
'fisica' => $row[2],
'moral' => $row[3],
'fecha_de_inicio_de_vigencia' => $row[4] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[4])) :
null,
'fecha_de_fin_de_vigencia' => $row[5] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[5])) :
null,
];
$batchData[] = $requestArray;
$processedRows++;
if (count($batchData) >= $batchSize) {
$this->insertBatch($batchData);
$batchData = [];
}
}
if (!empty($batchData)) {
$this->insertBatch($batchData);
}
echo "\n\033[32mImport completed: Processed $processedRows rows.\033[0m\n";
}
private function insertBatch(array $batchData)
{
try {
DB::table('sat_regimen_fiscal')->upsert(
$batchData,
[
'c_regimen_fiscal',
],
[
'descripcion',
'fisica',
'moral',
'fecha_de_inicio_de_vigencia',
'fecha_de_fin_de_vigencia'
]
);
} catch (\Exception $e) {
echo "Error in batch: " . $e->getMessage() . "\n";
foreach ($batchData as $row) {
echo "Row data: " . json_encode($row) . "\n";
}
}
}
}

View File

@ -0,0 +1,81 @@
<?php
namespace Modules\Admin\App\Imports;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Concerns\ToCollection;
class SATUsoCFDIImport implements ToCollection
{
/**
* @param Collection $collection
*/
public function collection(Collection $collection)
{
$batchSize = 1000;
$batchData = [];
$processedRows = 0;
foreach ($collection as $key => $row) {
if ($key < 6 || !$row[1]) {
continue;
}
$requestArray = [
'c_uso_cfdi' => $row[0],
'descripcion' => $row[1],
'aplica_para_tipo_persona_fisica' => $row[2],
'aplica_para_tipo_persona_moral' => $row[3],
'fecha_inicio_de_vigencia' => $row[4] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[4])) :
null,
'fecha_fin_de_vigencia' => $row[5] ?
Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[5])) :
null,
'regimen_fiscal_receptor' => $row[6],
];
$batchData[] = $requestArray;
$processedRows++;
if (count($batchData) >= $batchSize) {
$this->insertBatch($batchData);
$batchData = [];
}
}
if (!empty($batchData)) {
$this->insertBatch($batchData);
}
echo "\n\033[32mImport completed: Processed $processedRows rows.\033[0m\n";
}
private function insertBatch(array $batchData)
{
try {
DB::table('sat_uso_cfdi')->upsert(
$batchData,
[
'c_uso_cfdi',
],
[
'descripcion',
'aplica_para_tipo_persona_fisica',
'aplica_para_tipo_persona_moral',
'fecha_inicio_de_vigencia',
'fecha_fin_de_vigencia',
'regimen_fiscal_receptor',
]
);
} catch (\Exception $e) {
echo "Error in batch: " . $e->getMessage() . "\n";
foreach ($batchData as $row) {
echo "Row data: " . json_encode($row) . "\n";
}
}
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Modules\Admin\App\Imports;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
use Maatwebsite\Excel\Concerns\WithConditionalSheets;
class SatCatalogsImport implements WithMultipleSheets
{
use WithConditionalSheets;
public function conditionalSheets(): array
{
return [
'c_FormaPago' => new SATFormaPagoImport(),
'c_Moneda' => new SATMonedaImport(),
'c_CodigoPostal_Parte_1' => new SATCodigoPostalImport(),
'c_CodigoPostal_Parte_2' => new SATCodigoPostalImport(),
'c_RegimenFiscal' => new SATRegimenFiscalImport(),
'c_Pais' => new SATPaisImport(),
'c_UsoCFDI' => new SATUsoCFDIImport(),
'C_Colonia_1' => new SATColoniaImport(),
'C_Colonia_2' => new SATColoniaImport(),
'C_Colonia_3' => new SATColoniaImport(),
'c_Estado' => new SATEstadoImport(),
'C_Localidad' => new SATLocalidadImport(),
'C_Municipio' => new SATMunicipioImport(),
];
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace Modules\Admin\App\Listeners;
use Illuminate\Auth\Events\Logout;
use Illuminate\Support\Facades\Log;
use Modules\Admin\App\Services\VuexyAdminService;
class ClearUserCache
{
/**
* Handle the event.
*
* @return void
*/
public function handle(Logout $event)
{
if ($event->user) {
VuexyAdminService::clearUserMenuCache();
VuexyAdminService::clearSearchMenuCache();
VuexyAdminService::clearQuickLinksCache();
VuexyAdminService::clearNotificationsCache();
}
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace Modules\Admin\App\Listeners;
use Illuminate\Auth\Events\Login;
use Illuminate\Support\Facades\Mail;
use Modules\Admin\App\Models\UserLogin;
class HandleUserLogin
{
public function handle(Login $event)
{
// Guardar log en base de datos
UserLogin::create([
'user_id' => $event->user->id,
'ip_address' => request()->ip(),
'user_agent' => request()->header('User-Agent'),
]);
// Actualizar el último login
$event->user->update(['last_login_at' => now(), 'last_login_ip' => request()->ip()]);
// Enviar notificación de inicio de sesión
//Mail::to($event->user->email)->send(new LoginNotification($event->user));
}
}

View File

@ -0,0 +1,83 @@
<?php
namespace Modules\Admin\App\Livewire\AdminSettings;
use Livewire\Component;
use Livewire\WithFileUploads;
use Modules\Admin\App\Services\AdminSettingsService;
use Modules\Admin\App\Services\AdminTemplateService;
class ApplicationSettings extends Component
{
use WithFileUploads;
private $targetNotify = "#application-settings-card .notification-container";
public $admin_app_name,
$admin_image_logo,
$admin_image_logo_dark;
public $upload_image_logo,
$upload_image_logo_dark;
public function mount()
{
$this->loadSettings();
}
public function loadSettings($clearcache = false)
{
$this->upload_image_logo = null;
$this->upload_image_logo_dark = null;
$adminTemplateService = app(AdminTemplateService::class);
if ($clearcache) {
$adminTemplateService->clearAdminVarsCache();
}
// Obtener los valores de las configuraciones de la base de datos
$settings = $adminTemplateService->getAdminVars();
$this->admin_app_name = $settings['app_name'];
$this->admin_image_logo = $settings['image_logo']['large'];
$this->admin_image_logo_dark = $settings['image_logo']['large_dark'];
}
public function save()
{
$this->validate([
'admin_app_name' => 'required|string|max:255',
'upload_image_logo' => 'nullable|image|mimes:jpeg,png,jpg,svg,webp|max:20480',
'upload_image_logo_dark' => 'nullable|image|mimes:jpeg,png,jpg,svg,webp|max:20480',
]);
$adminSettingsService = app(AdminSettingsService::class);
// Guardar título del App en configuraciones
$adminSettingsService->updateSetting('admin_app_name', $this->admin_app_name);
// Procesar favicon si se ha cargado una imagen
if ($this->upload_image_logo) {
$adminSettingsService->processAndSaveImageLogo($this->upload_image_logo);
}
if ($this->upload_image_logo_dark) {
$adminSettingsService->processAndSaveImageLogo($this->upload_image_logo_dark, 'dark');
}
$this->loadSettings(true);
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.'
);
}
public function render()
{
return view('admin::livewire.admin-settings.application-settings');
}
}

View File

@ -0,0 +1,84 @@
<?php
namespace Modules\Admin\App\Livewire\AdminSettings;
use Livewire\Component;
use Livewire\WithFileUploads;
use Modules\Admin\App\Services\AdminSettingsService;
use Modules\Admin\App\Services\AdminTemplateService;
class GeneralSettings extends Component
{
use WithFileUploads;
private $targetNotify = "#general-settings-card .notification-container";
public $admin_title;
public $admin_favicon_16x16,
$admin_favicon_76x76,
$admin_favicon_120x120,
$admin_favicon_152x152,
$admin_favicon_180x180,
$admin_favicon_192x192;
public $upload_image_favicon;
public function mount()
{
$this->loadSettings();
}
public function loadSettings($clearcache = false)
{
$this->upload_image_favicon = null;
$adminTemplateService = app(AdminTemplateService::class);
if ($clearcache) {
$adminTemplateService->clearAdminVarsCache();
}
// Obtener los valores de las configuraciones de la base de datos
$settings = $adminTemplateService->getAdminVars();
$this->admin_title = $settings['title'];
$this->admin_favicon_16x16 = $settings['favicon']['16x16'];
$this->admin_favicon_76x76 = $settings['favicon']['76x76'];
$this->admin_favicon_120x120 = $settings['favicon']['120x120'];
$this->admin_favicon_152x152 = $settings['favicon']['152x152'];
$this->admin_favicon_180x180 = $settings['favicon']['180x180'];
$this->admin_favicon_192x192 = $settings['favicon']['192x192'];
}
public function save()
{
$this->validate([
'admin_title' => 'required|string|max:255',
'upload_image_favicon' => 'nullable|image|mimes:jpeg,png,jpg,svg,webp|max:20480',
]);
$adminSettingsService = app(AdminSettingsService::class);
// Guardar título del sitio en configuraciones
$adminSettingsService->updateSetting('admin_title', $this->admin_title);
// Procesar favicon si se ha cargado una imagen
if ($this->upload_image_favicon) {
$adminSettingsService->processAndSaveFavicon($this->upload_image_favicon);
}
$this->loadSettings(true);
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.'
);
}
public function render()
{
return view('admin::livewire.admin-settings.general-settings');
}
}

View File

@ -0,0 +1,118 @@
<?php
namespace Modules\Admin\App\Livewire\AdminSettings;
use Livewire\Component;
use Modules\Admin\App\Services\AdminTemplateService;
use Modules\Admin\App\Services\GlobalSettingsService;
class InterfaceSettings extends Component
{
private $targetNotify = "#interface-settings-card .notification-container";
public $vuexy_myLayout,
$vuexy_myTheme,
$vuexy_myStyle,
$vuexy_hasCustomizer,
$vuexy_displayCustomizer,
$vuexy_contentLayout,
$vuexy_navbarType,
$vuexy_footerFixed,
$vuexy_menuFixed,
$vuexy_menuCollapsed,
$vuexy_headerType,
$vuexy_showDropdownOnHover,
$vuexy_authViewMode,
$vuexy_maxQuickLinks;
public function mount()
{
$this->loadSettings();
}
public function loadSettings()
{
$adminTemplateService = app(AdminTemplateService::class);
// Obtener los valores de las configuraciones de la base de datos
$settings = $adminTemplateService->getVuexyCustomizerVars();
$this->vuexy_myLayout = $settings['myLayout'];
$this->vuexy_myTheme = $settings['myTheme'];
$this->vuexy_myStyle = $settings['myStyle'];
$this->vuexy_hasCustomizer = $settings['hasCustomizer'];
$this->vuexy_displayCustomizer = $settings['displayCustomizer'];
$this->vuexy_contentLayout = $settings['contentLayout'];
$this->vuexy_navbarType = $settings['navbarType'];
$this->vuexy_footerFixed = $settings['footerFixed'];
$this->vuexy_menuFixed = $settings['menuFixed'];
$this->vuexy_menuCollapsed = $settings['menuCollapsed'];
$this->vuexy_headerType = $settings['headerType'];
$this->vuexy_showDropdownOnHover = $settings['showDropdownOnHover'];
$this->vuexy_authViewMode = $settings['authViewMode'];
$this->vuexy_maxQuickLinks = $settings['maxQuickLinks'];
}
public function save()
{
$this->validate([
'vuexy_maxQuickLinks' => 'required|integer|min:2|max:20',
]);
$globalSettingsService = app(GlobalSettingsService::class);
// Guardar configuraciones
$globalSettingsService->updateSetting('config.custom.custom.myLayout', $this->vuexy_myLayout);
$globalSettingsService->updateSetting('config.custom.custom.myTheme', $this->vuexy_myTheme);
$globalSettingsService->updateSetting('config.custom.custom.myStyle', $this->vuexy_myStyle);
$globalSettingsService->updateSetting('config.custom.custom.hasCustomizer', $this->vuexy_hasCustomizer);
$globalSettingsService->updateSetting('config.custom.custom.displayCustomizer', $this->vuexy_displayCustomizer);
$globalSettingsService->updateSetting('config.custom.custom.contentLayout', $this->vuexy_contentLayout);
$globalSettingsService->updateSetting('config.custom.custom.navbarType', $this->vuexy_navbarType);
$globalSettingsService->updateSetting('config.custom.custom.footerFixed', $this->vuexy_footerFixed);
$globalSettingsService->updateSetting('config.custom.custom.menuFixed', $this->vuexy_menuFixed);
$globalSettingsService->updateSetting('config.custom.custom.menuCollapsed', $this->vuexy_menuCollapsed);
$globalSettingsService->updateSetting('config.custom.custom.headerType', $this->vuexy_headerType);
$globalSettingsService->updateSetting('config.custom.custom.showDropdownOnHover', $this->vuexy_showDropdownOnHover);
$globalSettingsService->updateSetting('config.custom.custom.authViewMode', $this->vuexy_authViewMode);
$globalSettingsService->updateSetting('config.custom.custom.maxQuickLinks', $this->vuexy_maxQuickLinks);
$globalSettingsService->clearSystemConfigCache();
// Refrescar el componente actual
$this->dispatch('clearLocalStoregeTemplateCustomizer');
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.',
deferReload: true
);
}
public function clearCustomConfig()
{
$globalSettingsService = app(GlobalSettingsService::class);
$globalSettingsService->clearVuexyCustomConfig();
// Refrescar el componente actual
$this->dispatch('clearLocalStoregeTemplateCustomizer');
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.',
deferReload: true
);
}
public function render()
{
return view('admin::livewire.admin-settings.interface-settings');
}
}

View File

@ -0,0 +1,106 @@
<?php
namespace Modules\Admin\App\Livewire\AdminSettings;
use Livewire\Component;
use Modules\Admin\App\Services\GlobalSettingsService;
class MailSenderResponseSettings extends Component
{
private $targetNotify = "#mail-sender-response-settings-card .notification-container";
public $from_address,
$from_name,
$reply_to_method,
$reply_to_email,
$reply_to_name;
protected $listeners = ['saveMailSenderResponseSettings' => 'save'];
const REPLY_EMAIL_CREATOR = 1;
const REPLY_EMAIL_SENDER = 2;
const REPLY_EMAIL_CUSTOM = 3;
public $reply_email_options = [
self::REPLY_EMAIL_CREATOR => 'Responder al creador del documento',
self::REPLY_EMAIL_SENDER => 'Responder a quien envía el documento',
self::REPLY_EMAIL_CUSTOM => 'Definir dirección de correo electrónico',
];
public function mount()
{
$this->loadSettings();
}
public function loadSettings()
{
$globalSettingsService = app(GlobalSettingsService::class);
// Obtener los valores de las configuraciones de la base de datos
$settings = $globalSettingsService->getMailSystemConfig();
$this->from_address = $settings['from']['address'];
$this->from_name = $settings['from']['name'];
$this->reply_to_method = $settings['reply_to']['method'];
$this->reply_to_email = $settings['reply_to']['email'];
$this->reply_to_name = $settings['reply_to']['name'];
}
public function save()
{
$this->validate([
'from_address' => 'required|email',
'from_name' => 'required|string|max:255',
'reply_to_method' => 'required|string|max:255',
], [
'from_address.required' => 'El campo de correo electrónico es obligatorio.',
'from_address.email' => 'El formato del correo electrónico no es válido.',
'from_name.required' => 'El nombre es obligatorio.',
'from_name.string' => 'El nombre debe ser una cadena de texto.',
'from_name.max' => 'El nombre no puede tener más de 255 caracteres.',
'reply_to_method.required' => 'El método de respuesta es obligatorio.',
'reply_to_method.string' => 'El método de respuesta debe ser una cadena de texto.',
'reply_to_method.max' => 'El método de respuesta no puede tener más de 255 caracteres.',
]);
if ($this->reply_to_method == self::REPLY_EMAIL_CUSTOM) {
$this->validate([
'reply_to_email' => ['required', 'email'],
'reply_to_name' => ['required', 'string', 'max:255'],
], [
'reply_to_email.required' => 'El correo de respuesta es obligatorio.',
'reply_to_email.email' => 'El formato del correo de respuesta no es válido.',
'reply_to_name.required' => 'El nombre de respuesta es obligatorio.',
'reply_to_name.string' => 'El nombre de respuesta debe ser una cadena de texto.',
'reply_to_name.max' => 'El nombre de respuesta no puede tener más de 255 caracteres.',
]);
}
$globalSettingsService = app(GlobalSettingsService::class);
// Guardar título del App en configuraciones
$globalSettingsService->updateSetting('mail.from.address', $this->from_address);
$globalSettingsService->updateSetting('mail.from.name', $this->from_name);
$globalSettingsService->updateSetting('mail.reply_to.method', $this->reply_to_method);
$globalSettingsService->updateSetting('mail.reply_to.email', $this->reply_to_method == self::REPLY_EMAIL_CUSTOM ? $this->reply_to_email : '');
$globalSettingsService->updateSetting('mail.reply_to.name', $this->reply_to_method == self::REPLY_EMAIL_CUSTOM ? $this->reply_to_name : '');
$globalSettingsService->clearMailSystemConfigCache();
$this->loadSettings();
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.',
);
}
public function render()
{
return view('admin::livewire.admin-settings.mail-sender-response-settings');
}
}

View File

@ -0,0 +1,175 @@
<?php
namespace Modules\Admin\App\Livewire\AdminSettings;
use Livewire\Component;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Crypt;
use Symfony\Component\Mailer\Transport;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mime\Email;
use Modules\Admin\App\Services\GlobalSettingsService;
class MailSmtpSettings extends Component
{
private $targetNotify = "#mail-smtp-settings-card .notification-container";
public $change_smtp_settings,
$host,
$port,
$encryption,
$username,
$password;
public $save_button_disabled;
protected $listeners = [
'loadSettings',
'testSmtpConnection',
];
// the list of smtp_encryption values that can be stored in table
const SMTP_ENCRYPTION_SSL = 'SSL';
const SMTP_ENCRYPTION_TLS = 'TLS';
const SMTP_ENCRYPTION_NONE = 'none';
public $encryption_options = [
self::SMTP_ENCRYPTION_SSL => 'SSL (Secure Sockets Layer)',
self::SMTP_ENCRYPTION_TLS => 'TLS (Transport Layer Security)',
self::SMTP_ENCRYPTION_NONE => 'Sin encriptación (No recomendado)',
];
public $rules = [
[
'host' => 'nullable|string|max:255',
'port' => 'nullable|integer',
'encryption' => 'nullable|string',
'username' => 'nullable|string|max:255',
'password' => 'nullable|string|max:255',
],
[
'host.string' => 'El servidor SMTP debe ser una cadena de texto.',
'host.max' => 'El servidor SMTP no puede exceder los 255 caracteres.',
'port.integer' => 'El puerto SMTP debe ser un número entero.',
'encryption.string' => 'El tipo de encriptación SMTP debe ser una cadena de texto.',
'username.string' => 'El nombre de usuario SMTP debe ser una cadena de texto.',
'username.max' => 'El nombre de usuario SMTP no puede exceder los 255 caracteres.',
'password.string' => 'La contraseña SMTP debe ser una cadena de texto.',
'password.max' => 'La contraseña SMTP no puede exceder los 255 caracteres.',
]
];
public function mount()
{
$this->loadSettings();
}
public function loadSettings()
{
$globalSettingsService = app(GlobalSettingsService::class);
// Obtener los valores de las configuraciones de la base de datos
$settings = $globalSettingsService->getMailSystemConfig();
$this->change_smtp_settings = false;
$this->save_button_disabled = true;
$this->host = $settings['mailers']['smtp']['host'];
$this->port = $settings['mailers']['smtp']['port'];
$this->encryption = $settings['mailers']['smtp']['encryption'];
$this->username = $settings['mailers']['smtp']['username'];
$this->password = null;
}
public function save()
{
$this->validate($this->rules[0]);
$globalSettingsService = app(GlobalSettingsService::class);
// Guardar título del App en configuraciones
$globalSettingsService->updateSetting('mail.mailers.smtp.host', $this->host);
$globalSettingsService->updateSetting('mail.mailers.smtp.port', $this->port);
$globalSettingsService->updateSetting('mail.mailers.smtp.encryption', $this->encryption);
$globalSettingsService->updateSetting('mail.mailers.smtp.username', $this->username);
$globalSettingsService->updateSetting('mail.mailers.smtp.password', Crypt::encryptString($this->password));
$globalSettingsService->clearMailSystemConfigCache();
$this->loadSettings();
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.'
);
}
public function testSmtpConnection()
{
// Validar los datos del formulario
$this->validate($this->rules[0]);
try {
// Verificar la conexión SMTP
if ($this->validateSMTPConnection()) {
$this->save_button_disabled = false;
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Conexión SMTP exitosa, se guardó los cambios exitosamente.',
);
}
} catch (\Exception $e) {
// Captura y maneja errores de conexión SMTP
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'danger',
message: 'Error en la conexión SMTP: ' . $e->getMessage(),
notificationTimeout: 15000 // Timeout personalizado
);
}
}
private function validateSMTPConnection()
{
$dsn = sprintf(
'smtp://%s:%s@%s:%s?encryption=%s',
urlencode($this->username), // Codificar nombre de usuario
urlencode($this->password), // Codificar contraseña
$this->host, // Host SMTP
$this->port, // Puerto SMTP
$this->encryption // Encriptación (tls o ssl)
);
// Crear el transportador usando el DSN
$transport = Transport::fromDsn($dsn);
// Crear el mailer con el transportador personalizado
$mailer = new Mailer($transport);
// Enviar un correo de prueba
$email = (new Email())
->from($this->username) // Dirección de correo del remitente
->to(env('MAIL_SANDBOX')) // Dirección de correo de destino
->subject(Config::get('app.name') . ' - Correo de prueba')
->text('Este es un correo de prueba para verificar la conexión SMTP.');
// Enviar el correo
$mailer->send($email);
return true;
}
public function render()
{
return view('admin::livewire.admin-settings.mail-smtp-settings');
}
}

View File

@ -0,0 +1,212 @@
<?php
namespace Modules\Admin\App\Livewire\Cache;
use Livewire\Component;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
class CacheFunctions extends Component
{
private $targetNotify = "#cache-functions-card .notification-container";
public $cacheCounts = [
'general' => 0,
'config' => 0,
'routes' => 0,
'views' => 0,
'events' => 0,
];
protected $listeners = [
'reloadCacheFunctionsStatsEvent' => 'reloadCacheStats',
];
public function mount()
{
$this->reloadCacheStats(false);
}
public function reloadCacheStats($notify = true)
{
$cacheDriver = config('cache.default'); // Obtiene el driver configurado para caché
// Caché General
switch ($cacheDriver) {
case 'memcached':
try {
$cacheStore = Cache::getStore()->getMemcached();
$stats = $cacheStore->getStats();
$this->cacheCounts['general'] = array_sum(array_column($stats, 'curr_items')); // Total de claves en Memcached
} catch (\Exception $e) {
$this->cacheCounts['general'] = 'Error obteniendo datos de Memcached';
}
break;
case 'redis':
try {
$prefix = config('cache.prefix'); // Asegúrate de agregar el sufijo correcto si es necesario
$keys = Redis::connection('cache')->keys($prefix . '*');
$this->cacheCounts['general'] = count($keys); // Total de claves en Redis
} catch (\Exception $e) {
$this->cacheCounts['general'] = 'Error obteniendo datos de Redis';
}
break;
case 'database':
try {
$this->cacheCounts['general'] = DB::table('cache')->count(); // Total de registros en la tabla de caché
} catch (\Exception $e) {
$this->cacheCounts['general'] = 'Error obteniendo datos de la base de datos';
}
break;
case 'file':
try {
$cachePath = config('cache.stores.file.path');
$files = glob($cachePath . '/*');
$this->cacheCounts['general'] = count($files);
} catch (\Exception $e) {
$this->cacheCounts['general'] = 'Error obteniendo datos de archivos';
}
break;
default:
$this->cacheCounts['general'] = 'Driver de caché no soportado';
}
// Configuración
$this->cacheCounts['config'] = file_exists(base_path('bootstrap/cache/config.php')) ? 1 : 0;
// Rutas
$this->cacheCounts['routes'] = count(glob(base_path('bootstrap/cache/routes-*.php'))) > 0 ? 1 : 0;
// Vistas
$this->cacheCounts['views'] = count(glob(storage_path('framework/views/*')));
// Configuración
$this->cacheCounts['events'] = file_exists(base_path('bootstrap/cache/events.php')) ? 1 : 0;
if ($notify) {
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han recargado los estadísticos de caché.'
);
}
}
public function clearLaravelCache()
{
Artisan::call('cache:clear');
sleep(1);
$this->response('Se han limpiado las cachés de la aplicación.', 'warning');
}
public function clearConfigCache()
{
Artisan::call('config:clear');
$this->response('Se ha limpiado la cache de la configuración de Laravel.', 'warning');
}
public function configCache()
{
Artisan::call('config:cache');
}
public function clearRouteCache()
{
Artisan::call('route:clear');
$this->response('Se han limpiado las rutas de Laravel.', 'warning');
}
public function cacheRoutes()
{
Artisan::call('route:cache');
}
public function clearViewCache()
{
Artisan::call('view:clear');
$this->response('Se han limpiado las vistas de Laravel.', 'warning');
}
public function cacheViews()
{
Artisan::call('view:cache');
$this->response('Se han cacheado las vistas de Laravel.');
}
public function clearEventCache()
{
Artisan::call('event:clear');
$this->response('Se han limpiado los eventos de Laravel.', 'warning');
}
public function cacheEvents()
{
Artisan::call('event:cache');
$this->response('Se han cacheado los eventos de Laravel.');
}
public function optimizeClear()
{
Artisan::call('optimize:clear');
$this->response('Se han optimizado todos los cachés de Laravel.');
}
public function resetPermissionCache()
{
Artisan::call('permission:cache-reset');
$this->response('Se han limpiado los cachés de permisos.', 'warning');
}
public function clearResetTokens()
{
Artisan::call('auth:clear-resets');
$this->response('Se han limpiado los tokens de reseteo de contraseña.', 'warning');
}
/**
* Genera una respuesta estandarizada.
*/
private function response(string $message, string $type = 'success'): void
{
$this->reloadCacheStats(false);
$this->dispatch(
'notification',
target: $this->targetNotify,
type: $type,
message: $message,
);
$this->dispatch('reloadCacheStatsEvent', notify: false);
$this->dispatch('reloadSessionStatsEvent', notify: false);
$this->dispatch('reloadRedisStatsEvent', notify: false);
$this->dispatch('reloadMemcachedStatsEvent', notify: false);
}
public function render()
{
return view('admin::livewire.cache.cache-functions');
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace Modules\Admin\App\Livewire\Cache;
use Livewire\Component;
use Modules\Admin\App\Services\CacheConfigService;
use Modules\Admin\App\Services\CacheManagerService;
class CacheStats extends Component
{
private $targetNotify = "#cache-stats-card .notification-container";
public $cacheConfig = [];
public $cacheStats = [];
protected $listeners = ['reloadCacheStatsEvent' => 'reloadCacheStats'];
public function mount(CacheConfigService $cacheConfigService)
{
$this->cacheConfig = $cacheConfigService->getConfig();
$this->reloadCacheStats(false);
}
public function reloadCacheStats($notify = true)
{
$cacheManagerService = new CacheManagerService();
$this->cacheStats = $cacheManagerService->getCacheStats();
if ($notify) {
$this->dispatch(
'notification',
target: $this->targetNotify,
type: $this->cacheStats['status'],
message: $this->cacheStats['message']
);
}
}
public function clearCache()
{
$cacheManagerService = new CacheManagerService();
$message = $cacheManagerService->clearCache();
$this->reloadCacheStats(false);
$this->dispatch(
'notification',
target: $this->targetNotify,
type: $message['status'],
message: $message['message'],
);
$this->dispatch('reloadRedisStatsEvent', notify: false);
$this->dispatch('reloadMemcachedStatsEvent', notify: false);
$this->dispatch('reloadCacheFunctionsStatsEvent', notify: false);
}
public function render()
{
return view('admin::livewire.cache.cache-stats');
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace Modules\Admin\App\Livewire\Cache;
use Livewire\Component;
use Modules\Admin\App\Services\CacheManagerService;
class MemcachedStats extends Component
{
private $driver = 'memcached';
private $targetNotify = "#memcached-stats-card .notification-container";
public $memcachedStats = [];
protected $listeners = ['reloadMemcachedStatsEvent' => 'reloadCacheStats'];
public function mount()
{
$this->reloadCacheStats(false);
}
public function reloadCacheStats($notify = true)
{
$cacheManagerService = new CacheManagerService($this->driver);
$memcachedStats = $cacheManagerService->getMemcachedStats();
$this->memcachedStats = $memcachedStats['info'];
if ($notify) {
$this->dispatch(
'notification',
target: $this->targetNotify,
type: $memcachedStats['status'],
message: $memcachedStats['message']
);
}
}
public function clearCache()
{
$cacheManagerService = new CacheManagerService($this->driver);
$message = $cacheManagerService->clearCache();
$this->reloadCacheStats(false);
$this->dispatch(
'notification',
target: $this->targetNotify,
type: $message['status'],
message: $message['message'],
);
$this->dispatch('reloadCacheStatsEvent', notify: false);
$this->dispatch('reloadSessionStatsEvent', notify: false);
$this->dispatch('reloadCacheFunctionsStatsEvent', notify: false);
}
public function render()
{
return view('admin::livewire.cache.memcached-stats');
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace Modules\Admin\App\Livewire\Cache;
use Livewire\Component;
use Modules\Admin\App\Services\CacheManagerService;
class RedisStats extends Component
{
private $driver = 'redis';
private $targetNotify = "#redis-stats-card .notification-container";
public $redisStats = [];
protected $listeners = ['reloadRedisStatsEvent' => 'reloadCacheStats'];
public function mount()
{
$this->reloadCacheStats(false);
}
public function reloadCacheStats($notify = true)
{
$cacheManagerService = new CacheManagerService($this->driver);
$redisStats = $cacheManagerService->getRedisStats();
$this->redisStats = $redisStats['info'];
if ($notify) {
$this->dispatch(
'notification',
target: $this->targetNotify,
type: $redisStats['status'],
message: $redisStats['message']
);
}
}
public function clearCache()
{
$cacheManagerService = new CacheManagerService($this->driver);
$message = $cacheManagerService->clearCache();
$this->reloadCacheStats(false);
$this->dispatch(
'notification',
target: $this->targetNotify,
type: $message['status'],
message: $message['message'],
);
$this->dispatch('reloadCacheStatsEvent', notify: false);
$this->dispatch('reloadSessionStatsEvent', notify: false);
$this->dispatch('reloadCacheFunctionsStatsEvent', notify: false);
}
public function render()
{
return view('admin::livewire.cache.redis-stats');
}
}

View File

@ -0,0 +1,63 @@
<?php
namespace Modules\Admin\App\Livewire\Cache;
use Livewire\Component;
use Modules\Admin\App\Services\CacheConfigService;
use Modules\Admin\App\Services\SessionManagerService;
class SessionStats extends Component
{
private $targetNotify = "#session-stats-card .notification-container";
public $cacheConfig = [];
public $sessionStats = [];
protected $listeners = ['reloadSessionStatsEvent' => 'reloadSessionStats'];
public function mount(CacheConfigService $cacheConfigService)
{
$this->cacheConfig = $cacheConfigService->getConfig();
$this->reloadSessionStats(false);
}
public function reloadSessionStats($notify = true)
{
$sessionManagerService = new SessionManagerService();
$this->sessionStats = $sessionManagerService->getSessionStats();
if ($notify) {
$this->dispatch(
'notification',
target: $this->targetNotify,
type: $this->sessionStats['status'],
message: $this->sessionStats['message']
);
}
}
public function clearSessions()
{
$sessionManagerService = new SessionManagerService();
$message = $sessionManagerService->clearSessions();
$this->reloadSessionStats(false);
$this->dispatch(
'notification',
target: $this->targetNotify,
type: $message['status'],
message: $message['message'],
);
$this->dispatch('reloadRedisStatsEvent', notify: false);
$this->dispatch('reloadMemcachedStatsEvent', notify: false);
}
public function render()
{
return view('admin::livewire.cache.session-stats');
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace Modules\Admin\App\Livewire\Rbac;
use Spatie\Permission\Models\Role;
use Livewire\Component;
class PermissionsIndex extends Component
{
public $roles_html_select;
public $rows_roles;
public function render()
{
// Generamos Select y estilos HTML de roles
$this->roles_html_select = "<select id=\"UserRole\" class=\"form-select text-capitalize\"><option value=\"\"> Selecciona un rol </option>";
foreach (Role::all() as $role) {
$this->rows_roles[$role->name] = "<span class=\"badge bg-label-{$role->style} m-1\">{$role->name}</span>";
$this->roles_html_select .= "<option value=\"{$role->name}\" class=\"text-capitalize\">{$role->name}</option>";
}
$this->roles_html_select .= "</select>";
return view('admin::livewire.rbac.permissions-index');
}
}

View File

@ -0,0 +1,182 @@
<?php
namespace Modules\Admin\App\Livewire\Rbac;
use Illuminate\Support\Facades\Auth;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Livewire\Component;
class RoleCards extends Component
{
public $roles = [];
public $permissions = [];
public $roleId;
public $name;
public $style;
public $title;
public $btn_submit_text;
public $permissionsInputs = [];
public $destroyRoleId;
protected $listeners = ['saveRole', 'deleteRole'];
public function mount()
{
$this->loadRolesAndPermissions();
$this->dispatch('reloadForm');
}
private function loadRolesAndPermissions()
{
$this->roles = Auth::user()->hasRole('SuperAdmin') ?
Role::all() :
Role::where('name', '!=', 'SuperAdmin')->get();
// Obtener todos los permisos
$permissions = Permission::all()->map(function ($permission) {
$name = $permission->name;
$action = substr($name, strrpos($name, '.') + 1);
return [
'group_name' => $permission->group_name,
'sub_group_name' => $permission->sub_group_name,
$action => $name // Agregar la acción directamente al array
];
})->groupBy('group_name'); // Agrupar los permisos por grupo
// Procesar los permisos agrupados para cargarlos en el componente
$permissionsInputs = [];
$this->permissions = $permissions->map(function ($groupPermissions) use (&$permissionsInputs) {
$permission = [
'group_name' => $groupPermissions[0]['group_name'], // Tomar el grupo del primer permiso del grupo
'sub_group_name' => $groupPermissions[0]['sub_group_name'], // Tomar la descripción del primer permiso del grupo
];
// Agregar todas las acciones al permissionsInputs y al permission
foreach ($groupPermissions as $permissionData) {
foreach ($permissionData as $key => $value) {
if ($key !== 'sub_group_name' && $key !== 'group_name') {
$permissionsInputs[str_replace('.', '_', $value)] = false;
$permission[$key] = $value;
}
}
}
return $permission;
});
$this->permissionsInputs = $permissionsInputs;
}
public function loadRoleData($action, $roleId = false)
{
$this->resetForm();
$this->title = 'Agregar un nuevo rol';
$this->btn_submit_text = 'Crear nuevo rol';
if ($roleId) {
$role = Role::findOrFail($roleId);
switch ($action) {
case 'view':
$this->title = $role->name;
$this->name = $role->name;
$this->style = $role->style;
$this->dispatch('deshabilitarFormulario');
break;
case 'update':
$this->title = 'Editar rol';
$this->btn_submit_text = 'Guardar cambios';
$this->roleId = $roleId;
$this->name = $role->name;
$this->style = $role->style;
$this->dispatch('habilitarFormulario');
break;
case 'clone':
$this->style = $role->style;
$this->dispatch('habilitarFormulario');
break;
default:
break;
}
foreach ($role->permissions as $permission) {
$this->permissionsInputs[str_replace('.', '_', $permission->name)] = true;
}
}
$this->dispatch('reloadForm');
}
public function loadDestroyRoleData() {}
public function saveRole()
{
$permissions = [];
foreach ($this->permissionsInputs as $permission => $value) {
if ($value === true)
$permissions[] = str_replace('_', '.', $permission);
}
if ($this->roleId) {
$role = Role::find($this->roleId);
$role->name = $this->name;
$role->style = $this->style;
$role->save();
$role->syncPermissions($permissions);
} else {
$role = Role::create([
'name' => $this->name,
'style' => $this->style,
]);
$role->syncPermissions($permissions);
}
$this->loadRolesAndPermissions();
$this->dispatch('modalHide');
$this->dispatch('reloadForm');
}
public function deleteRole()
{
$role = Role::find($this->destroyRoleId);
if ($role)
$role->delete();
$this->loadRolesAndPermissions();
$this->dispatch('modalDeleteHide');
$this->dispatch('reloadForm');
}
private function resetForm()
{
$this->roleId = '';
$this->name = '';
$this->style = '';
foreach ($this->permissionsInputs as $key => $permission) {
$this->permissionsInputs[$key] = false;
}
}
public function render()
{
return view('admin::livewire.rbac.role-cards');
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Modules\Admin\App\Livewire\Users;
use Modules\Admin\App\Models\User;
use Livewire\Component;
class UserCount extends Component
{
public $total, $enabled, $disabled;
protected $listeners = ['refreshUserCount' => 'updateCounts'];
public function mount()
{
$this->updateCounts();
}
public function updateCounts()
{
$this->total = User::count();
$this->enabled = User::where('status', User::STATUS_ENABLED)->count();
$this->disabled = User::where('status', User::STATUS_DISABLED)->count();
}
public function render()
{
return view('admin::livewire.users.user-count');
}
}

View File

@ -0,0 +1,115 @@
<?php
namespace Modules\Admin\App\Livewire\Users;
use Spatie\Permission\Models\Role;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use Modules\Admin\App\Models\User;
use Livewire\Component;
class UserTable extends Component
{
public $statuses;
public $status_options;
public $rows_roles = [];
public $roles_options = [];
public $roles_html_select;
public $total, $enabled, $disabled;
public $indexAlert;
public $userId, $name, $email, $password, $roles, $status, $photo, $src_photo;
public $modalTitle;
public $btnSubmitTxt;
public function mount()
{
$this->modalTitle = 'Crear usuario nuevo';
$this->btnSubmitTxt = 'Crear usuario';
$this->statuses = [
User::STATUS_ENABLED => ['title' => 'Activo', 'class' => 'badge bg-label-' . User::$statusListClass[User::STATUS_ENABLED]],
User::STATUS_DISABLED => ['title' => 'Deshabilitado', 'class' => 'badge bg-label-' . User::$statusListClass[User::STATUS_DISABLED]],
User::STATUS_REMOVED => ['title' => 'Eliminado', 'class' => 'badge bg-label-' . User::$statusListClass[User::STATUS_REMOVED]],
];
$roles = Role::whereNotIn('name', ['Patient', 'Doctor'])->get();
$this->roles_html_select = "<select id=\"UserRole\" class=\"form-select text-capitalize\"><option value=\"\"> Selecciona un rol </option>";
foreach ($roles as $role) {
$this->rows_roles[$role->name] = "<span class=\"badge bg-label-" . $role->style . " mx-1\">" . $role->name . "</span>";
if (Auth::user()->hasRole('SuperAdmin') || $role->name != 'SuperAdmin') {
$this->roles_html_select .= "<option value=\"" . $role->name . "\" class=\"text-capitalize\">" . $role->name . "</option>";
$this->roles_options[$role->name] = $role->name;
}
}
$this->roles_html_select .= "</select>";
$this->status_options = [
User::STATUS_ENABLED => User::$statusList[User::STATUS_ENABLED],
User::STATUS_DISABLED => User::$statusList[User::STATUS_DISABLED],
];
}
public function countUsers()
{
$this->total = User::count();
$this->enabled = User::where('status', User::STATUS_ENABLED)->count();
$this->disabled = User::where('status', User::STATUS_DISABLED)->count();
}
public function edit($id)
{
$user = User::findOrFail($id);
$this->indexAlert = '';
$this->modalTitle = 'Editar usuario: ' . $id;
$this->btnSubmitTxt = 'Guardar cambios';
$this->userId = $user->id;
$this->name = $user->name;
$this->email = $user->email;
$this->password = '';
$this->roles = $user->roles->pluck('name')->toArray();
$this->src_photo = $user->profile_photo_url;
$this->status = $user->status;
$this->dispatch('openModal');
}
public function delete($id)
{
$user = User::find($id);
if ($user) {
// Eliminar la imagen de perfil si existe
if ($user->profile_photo_path)
Storage::disk('public')->delete($user->profile_photo_path);
// Eliminar el usuario
$user->delete();
$this->indexAlert = '<div class="alert alert-warning alert-dismissible" role="alert">Se eliminó correctamente el usuario.<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
$this->dispatch('refreshUserCount');
$this->dispatch('afterDelete');
} else {
$this->indexAlert = '<div class="alert alert-danger alert-dismissible" role="alert">Usuario no encontrado.<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
}
}
public function render()
{
return view('admin::livewire.users.user-table', [
'users' => User::paginate(10),
]);
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace Modules\Admin\App\Livewire\WebsiteSettings;
use Livewire\Component;
use App\Services\WebsiteTemplateService;
use Modules\Admin\App\Services\WebsiteSettingsService;
class AnalyticsSettings extends Component
{
private $targetNotify = "#website-analytics-settings-card .notification-container";
public $google_analytics_enabled,
$google_analytics_id;
protected $listeners = ['saveAnalyticsSettings' => 'save'];
public function mount()
{
$this->loadSettings();
}
public function loadSettings()
{
$WebsiteTemplateService = app(WebsiteTemplateService::class);
// Obtener los valores de las configuraciones de la base de datos
$settings = $WebsiteTemplateService->getWebsiteVars('google');
$this->google_analytics_enabled = $settings['analytics']['enabled'];
$this->google_analytics_id = $settings['analytics']['id'];
}
public function save()
{
if ($this->google_analytics_enabled) {
$this->validate([
'google_analytics_id' => 'required|string|min:12|max:30',
]);
}
$websiteSettingsService = app(WebsiteSettingsService::class);
// Guardar título del App en configuraciones
$websiteSettingsService->updateSetting('google_analytics_enabled', $this->google_analytics_enabled);
$websiteSettingsService->updateSetting('google_analytics_id', $this->google_analytics_id);
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
$this->loadSettings();
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.'
);
}
public function render()
{
return view('admin::livewire.website-settings.analytics-settings');
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace Modules\Admin\App\Livewire\WebsiteSettings;
use Livewire\Component;
use App\Services\WebsiteTemplateService;
use Modules\Admin\App\Services\WebsiteSettingsService;
class ChatSettings extends Component
{
private $targetNotify = "#website-chat-settings-card .notification-container";
public $chat_provider,
$chat_whatsapp_number,
$chat_whatsapp_message;
protected $listeners = ['saveChatSettings' => 'save'];
public function mount()
{
$this->loadSettings();
}
public function loadSettings()
{
$websiteTemplateService = app(WebsiteTemplateService::class);
// Obtener los valores de las configuraciones de la base de datos
$settings = $websiteTemplateService->getWebsiteVars('chat');
$this->chat_provider = $settings['provider'];
$this->chat_whatsapp_number = $settings['whatsapp_number'];
$this->chat_whatsapp_message = $settings['whatsapp_message'];
}
public function save()
{
if ($this->chat_provider == 'whatsapp') {
$this->validate([
'chat_whatsapp_number' => 'required|string|max:20',
'chat_whatsapp_message' => 'required|string|max:255',
]);
}
$websiteSettingsService = app(WebsiteSettingsService::class);
// Guardar título del App en configuraciones
$websiteSettingsService->updateSetting('chat_provider', $this->chat_provider);
$websiteSettingsService->updateSetting('chat_whatsapp_number', preg_replace('/\D/', '', $this->chat_whatsapp_number));
$websiteSettingsService->updateSetting('chat_whatsapp_message', $this->chat_whatsapp_message);
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
$this->loadSettings();
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.'
);
}
public function render()
{
return view('admin::livewire.website-settings.chat-settings');
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace Modules\Admin\App\Livewire\WebsiteSettings;
use Livewire\Component;
use App\Services\WebsiteTemplateService;
use Modules\Admin\App\Services\WebsiteSettingsService;
class ContactFormSettings extends Component
{
private $targetNotify = "#website-contact-form-settings-card .notification-container";
public $contact_form_email,
$contact_form_email_cc,
$contact_form_subject;
protected $listeners = ['saveContactFormSettings' => 'save'];
public function mount()
{
$this->loadSettings();
}
public function loadSettings()
{
$websiteTemplateService = app(WebsiteTemplateService::class);
// Obtener los valores de las configuraciones de la base de datos
$settings = $websiteTemplateService->getWebsiteVars();
$this->contact_form_email = $settings['contact']['form']['email'];
$this->contact_form_email_cc = $settings['contact']['form']['email_cc'];
$this->contact_form_subject = $settings['contact']['form']['subject'];
}
public function save()
{
$this->validate([
'contact_form_email' => 'required|email',
'contact_form_email_cc' => 'nullable|email',
'contact_form_subject' => 'required|string'
]);
$websiteSettingsService = app(WebsiteSettingsService::class);
// Guardar título del App en configuraciones
$websiteSettingsService->updateSetting('contact_form_email', $this->contact_form_email);
$websiteSettingsService->updateSetting('contact_form_email_cc', $this->contact_form_email_cc);
$websiteSettingsService->updateSetting('contact_form_subject', $this->contact_form_subject);
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
$this->loadSettings();
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.'
);
}
public function render()
{
return view('admin::livewire.website-settings.contact-form-settings');
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace Modules\Admin\App\Livewire\WebsiteSettings;
use Livewire\Component;
use App\Services\WebsiteTemplateService;
use Modules\Admin\App\Services\WebsiteSettingsService;
class ContactInfoSettings extends Component
{
private $targetNotify = "#website-contact-info-settings-card .notification-container";
public $contact_phone_number,
$contact_phone_number_ext,
$contact_email;
protected $listeners = ['saveContactInfoSettings' => 'save'];
public function mount()
{
$this->loadSettings();
}
public function loadSettings()
{
$websiteTemplateService = app(WebsiteTemplateService::class);
// Obtener los valores de las configuraciones de la base de datos
$settings = $websiteTemplateService->getWebsiteVars();
$this->contact_phone_number = $settings['contact']['phone_number'];
$this->contact_phone_number_ext = $settings['contact']['phone_number_ext'];
$this->contact_email = $settings['contact']['email'];
}
public function save()
{
$this->validate([
'contact_phone_number' => ['nullable', 'string', 'max:20'],
'contact_phone_number_ext' => ['nullable', 'string', 'max:10'],
'contact_email' => ['nullable', 'email']
]);
$websiteSettingsService = app(WebsiteSettingsService::class);
// Guardar título del App en configuraciones
$websiteSettingsService->updateSetting('contact_phone_number', $this->contact_phone_number);
$websiteSettingsService->updateSetting('contact_phone_number_ext', $this->contact_phone_number_ext);
$websiteSettingsService->updateSetting('contact_email', $this->contact_email);
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
$this->loadSettings();
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.'
);
}
public function render()
{
return view('admin::livewire.website-settings.contact-info-settings');
}
}

View File

@ -0,0 +1,72 @@
<?php
namespace Modules\Admin\App\Livewire\WebsiteSettings;
use Livewire\Component;
use Livewire\WithFileUploads;
use App\Services\WebsiteTemplateService;
use Modules\Admin\App\Services\WebsiteSettingsService;
class FaviconSettings extends Component
{
use WithFileUploads;
private $targetNotify = "#website-favicon-settings-card .notification-container";
public $upload_image_favicon;
public $website_favicon_16x16,
$website_favicon_76x76,
$website_favicon_120x120,
$website_favicon_152x152,
$website_favicon_180x180,
$website_favicon_192x192;
public function mount()
{
$this->loadSettings();
}
public function loadSettings()
{
$websiteTemplateService = app(WebsiteTemplateService::class);
$this->upload_image_favicon = null;
// Obtener los valores de las configuraciones de la base de datos
$settings = $websiteTemplateService->getWebsiteVars();
$this->website_favicon_16x16 = $settings['favicon']['16x16'];
$this->website_favicon_76x76 = $settings['favicon']['76x76'];
$this->website_favicon_120x120 = $settings['favicon']['120x120'];
$this->website_favicon_152x152 = $settings['favicon']['152x152'];
$this->website_favicon_180x180 = $settings['favicon']['180x180'];
$this->website_favicon_192x192 = $settings['favicon']['192x192'];
}
public function save()
{
$this->validate([
'upload_image_favicon' => 'required|image|mimes:jpeg,png,jpg,svg,webp|max:20480',
]);
// Procesar favicon si se ha cargado una imagen
$websiteSettingsService = app(WebsiteSettingsService::class);
$websiteSettingsService->processAndSaveFavicon($this->upload_image_favicon);
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
$this->loadSettings();
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.'
);
}
public function render()
{
return view('admin::livewire.website-settings.favicon-settings');
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace Modules\Admin\App\Livewire\WebsiteSettings;
use Livewire\Component;
use Livewire\WithFileUploads;
use App\Services\WebsiteTemplateService;
use Modules\Admin\App\Services\WebsiteSettingsService;
class ImageLogoSettings extends Component
{
use WithFileUploads;
private $targetNotify = "#website-image-logo-settings-card .notification-container";
public $website_image_logo,
$website_image_logo_dark;
public $upload_image_logo,
$upload_image_logo_dark;
public function mount()
{
$this->loadSettings();
}
public function loadSettings()
{
$websiteTemplateService = app(WebsiteTemplateService::class);
$this->upload_image_logo = null;
$this->upload_image_logo_dark = null;
// Obtener los valores de las configuraciones de la base de datos
$settings = $websiteTemplateService->getWebsiteVars();
$this->website_image_logo = $settings['image_logo']['large'];
$this->website_image_logo_dark = $settings['image_logo']['large_dark'];
}
public function save()
{
$this->validate([
'upload_image_logo' => 'nullable|image|mimes:jpeg,png,jpg,svg,webp|max:20480',
'upload_image_logo_dark' => 'nullable|image|mimes:jpeg,png,jpg,svg,webp|max:20480',
]);
$websiteSettingsService = app(WebsiteSettingsService::class);
// Procesar favicon si se ha cargado una imagen
if ($this->upload_image_logo) {
$websiteSettingsService->processAndSaveImageLogo($this->upload_image_logo);
}
if ($this->upload_image_logo_dark) {
$websiteSettingsService->processAndSaveImageLogo($this->upload_image_logo_dark, 'dark');
}
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
$this->loadSettings();
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.'
);
}
public function render()
{
return view('admin::livewire.website-settings.image-logo-settings');
}
}

View File

@ -0,0 +1,108 @@
<?php
namespace Modules\Admin\App\Livewire\WebsiteSettings;
use Livewire\Component;
use App\Services\WebsiteTemplateService;
use Modules\Admin\App\Rules\NotEmptyHtml;
use Modules\Admin\App\Services\WebsiteSettingsService;
class LegalSettings extends Component
{
private $targetNotify = "#website-legal-settings-card .notification-container";
public $legalVars = [];
public $currentSection = null;
protected $listeners = [
'saveLegal' => 'save',
];
public function mount()
{
$this->loadSettings();
// Seleccionar la primera sección por defecto
$this->currentSection = array_key_first($this->legalVars);
}
function loadSettings()
{
$websiteTemplateService = app(WebsiteTemplateService::class);
switch ($this->currentSection) {
case 'legal_terminos_y_condiciones':
$this->legalVars['legal_terminos_y_condiciones'] = $websiteTemplateService->getLegalVars('legal_terminos_y_condiciones');
break;
case 'legal_aviso_de_privacidad':
$this->legalVars['legal_aviso_de_privacidad'] = $websiteTemplateService->getLegalVars('legal_aviso_de_privacidad');
break;
case 'legal_politica_de_devoluciones':
$this->legalVars['legal_politica_de_devoluciones'] = $websiteTemplateService->getLegalVars('legal_politica_de_devoluciones');
break;
case 'legal_politica_de_envios':
$this->legalVars['legal_politica_de_envios'] = $websiteTemplateService->getLegalVars('legal_politica_de_envios');
break;
case 'legal_politica_de_cookies':
$this->legalVars['legal_politica_de_cookies'] = $websiteTemplateService->getLegalVars('legal_politica_de_cookies');
break;
case 'legal_autorizaciones_y_licencias':
$this->legalVars['legal_autorizaciones_y_licencias'] = $websiteTemplateService->getLegalVars('legal_autorizaciones_y_licencias');
break;
case 'legal_informacion_comercial':
$this->legalVars['legal_informacion_comercial'] = $websiteTemplateService->getLegalVars('legal_informacion_comercial');
break;
case 'legal_consentimiento_para_el_login_de_terceros':
$this->legalVars['legal_consentimiento_para_el_login_de_terceros'] = $websiteTemplateService->getLegalVars('legal_consentimiento_para_el_login_de_terceros');
break;
case 'legal_leyendas_de_responsabilidad':
$this->legalVars['legal_leyendas_de_responsabilidad'] = $websiteTemplateService->getLegalVars('legal_leyendas_de_responsabilidad');
break;
default:
$this->legalVars = $websiteTemplateService->getLegalVars();
}
}
public function rules()
{
$rules = [];
if ($this->legalVars[$this->currentSection]['enabled']) {
$rules["legalVars.{$this->currentSection}.content"] = ['required', 'string', new NotEmptyHtml];
}
$rules["legalVars.{$this->currentSection}.enabled"] = 'boolean';
return $rules;
}
public function save()
{
$this->validate($this->rules());
$websiteSettingsService = app(WebsiteSettingsService::class);
$websiteSettingsService->updateSetting($this->currentSection . '_enabled', $this->legalVars[$this->currentSection]['enabled']);
$websiteSettingsService->updateSetting($this->currentSection . '_content', $this->legalVars[$this->currentSection]['content']);
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.'
);
}
public function render()
{
return view('admin::livewire.website-settings.legal-settings');
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace Modules\Admin\App\Livewire\WebsiteSettings;
use Livewire\Component;
use App\Services\WebsiteTemplateService;
use Modules\Admin\App\Services\WebsiteSettingsService;
class LocationSettings extends Component
{
private $targetNotify = "#website-location-settings-card .notification-container";
public $contact_direccion,
$contact_horario,
$contact_location_lat,
$contact_location_lng;
protected $listeners = ['saveLocationSettings' => 'save'];
public function mount()
{
$this->loadSettings();
}
public function loadSettings()
{
$websiteTemplateService = app(WebsiteTemplateService::class);
// Obtener los valores de las configuraciones de la base de datos
$settings = $websiteTemplateService->getWebsiteVars();
$this->contact_direccion = $settings['contact']['direccion'];
$this->contact_horario = $settings['contact']['horario'];
$this->contact_location_lat = $settings['contact']['location']['lat'];
$this->contact_location_lng = $settings['contact']['location']['lng'];
}
public function save()
{
$this->validate([
'contact_direccion' => ['nullable', 'string', 'max:255'],
'contact_horario' => ['nullable', 'string', 'max:255'],
'contact_location_lat' => ['nullable', 'numeric'],
'contact_location_lng' => ['nullable', 'numeric'],
]);
$websiteSettingsService = app(WebsiteSettingsService::class);
// Guardar título del App en configuraciones
$websiteSettingsService->updateSetting('contact_direccion', $this->contact_direccion);
$websiteSettingsService->updateSetting('contact_horario', $this->contact_horario);
$websiteSettingsService->updateSetting('contact_location_lat', $this->contact_location_lat);
$websiteSettingsService->updateSetting('contact_location_lng', $this->contact_location_lng);
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
$this->loadSettings();
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.'
);
}
public function render()
{
return view('admin::livewire.website-settings.location-settings');
}
}

View File

@ -0,0 +1,100 @@
<?php
namespace Modules\Admin\App\Livewire\WebsiteSettings;
use Livewire\Component;
use App\Services\WebsiteTemplateService;
use Modules\Admin\App\Services\WebsiteSettingsService;
class SocialSettings extends Component
{
private $targetNotify = "#website-social-settings-card .notification-container";
public $social_whatsapp,
$social_whatsapp_message,
$social_facebook,
$social_instagram,
$social_linkedin,
$social_tiktok,
$social_x_twitter,
$social_google,
$social_pinterest,
$social_youtube,
$social_vimeo;
protected $listeners = ['saveSocialSettings' => 'save'];
public function mount()
{
$this->loadSettings();
}
public function loadSettings()
{
$websiteTemplateService = app(WebsiteTemplateService::class);
// Obtener los valores de las configuraciones de la base de datos
$settings = $websiteTemplateService->getSocialVars();
$this->social_whatsapp = $settings['whatsapp'];
$this->social_whatsapp_message = $settings['whatsapp_message'];
$this->social_facebook = $settings['facebook'];
$this->social_instagram = $settings['instagram'];
$this->social_linkedin = $settings['linkedin'];
$this->social_tiktok = $settings['tiktok'];
$this->social_x_twitter = $settings['x_twitter'];
$this->social_google = $settings['google'];
$this->social_pinterest = $settings['pinterest'];
$this->social_youtube = $settings['youtube'];
$this->social_vimeo = $settings['vimeo'];
}
public function save()
{
$this->validate([
'social_whatsapp' => 'string|max:20',
'social_whatsapp_message' => 'string|max:255',
'social_facebook' => 'url',
'social_instagram' => 'url',
'social_linkedin' => 'url',
'social_tiktok' => 'url',
'social_x_twitter' => 'url',
'social_google' => 'url',
'social_pinterest' => 'url',
'social_youtube' => 'url',
'social_vimeo' => 'url',
]);
$websiteSettingsService = app(websiteSettingsService::class);
// Guardar título del App en configuraciones
$websiteSettingsService->updateSetting('social_whatsapp', preg_replace('/\D/', '', $this->social_whatsapp));
$websiteSettingsService->updateSetting('social_whatsapp_message', $this->social_whatsapp_message);
$websiteSettingsService->updateSetting('social_facebook', $this->social_facebook);
$websiteSettingsService->updateSetting('social_instagram', $this->social_instagram);
$websiteSettingsService->updateSetting('social_linkedin', $this->social_linkedin);
$websiteSettingsService->updateSetting('social_tiktok', $this->social_tiktok);
$websiteSettingsService->updateSetting('social_x_twitter', $this->social_x_twitter);
$websiteSettingsService->updateSetting('social_google', $this->social_google);
$websiteSettingsService->updateSetting('social_pinterest', $this->social_pinterest);
$websiteSettingsService->updateSetting('social_youtube', $this->social_youtube);
$websiteSettingsService->updateSetting('social_vimeo', $this->social_vimeo);
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
$this->loadSettings();
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.'
);
}
public function render()
{
return view('admin::livewire.website-settings.social-settings');
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace Modules\Admin\App\Livewire\WebsiteSettings;
use Livewire\Component;
use App\Services\WebsiteTemplateService;
class TemplateSettings extends Component
{
private $targetNotify = "#website-template-settings-card .notification-container";
public $website_tpl_style_switcher,
$website_tpl_footer_text;
protected $listeners = ['saveTemplateSettings' => 'save'];
public function mount()
{
$this->loadSettings();
}
public function loadSettings()
{
$websiteTemplateService = app(WebsiteTemplateService::class);
// Obtener los valores de las configuraciones de la base de datos
$settings = $websiteTemplateService->getWebsiteVars();
$this->website_tpl_style_switcher = $settings['template']['style_switcher'];
$this->website_tpl_footer_text = $settings['template']['footer_text'];
}
public function save()
{
$this->validate([
'website_tpl_footer_text' => 'nullable|string|max:50',
]);
$websiteTemplateService = app(WebsiteTemplateService::class);
// Guardar título del App en configuraciones
$websiteTemplateService->updateSetting('website_tpl_style_switcher', $this->website_tpl_style_switcher);
$websiteTemplateService->updateSetting('website_tpl_footer_text', $this->website_tpl_footer_text);
$websiteTemplateService->clearWebsiteVarsCache();
$this->loadSettings();
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.'
);
}
public function render()
{
return view('admin::livewire.website-settings.template-settings');
}
}

View File

@ -0,0 +1,62 @@
<?php
namespace Modules\Admin\App\Livewire\WebsiteSettings;
use Livewire\Component;
use App\Services\WebsiteTemplateService;
class WebsiteSettings extends Component
{
private $targetNotify = "#website-settings-card .notification-container";
public $website_title,
$website_description;
protected $listeners = ['saveWebsiteSettings' => 'save'];
public function mount()
{
$this->loadSettings();
}
public function loadSettings()
{
$websiteTemplateService = app(WebsiteTemplateService::class);
// Obtener los valores de las configuraciones de la base de datos
$settings = $websiteTemplateService->getWebsiteVars();
$this->website_title = $settings['title'];
$this->website_description = $settings['description'];
}
public function save()
{
$this->validate([
'website_title' => 'string|required|max:50',
'website_description' => 'string|max:160',
]);
$websiteTemplateService = app(WebsiteTemplateService::class);
// Guardar título del App en configuraciones
$websiteTemplateService->updateSetting('website_title', $this->website_title);
$websiteTemplateService->updateSetting('website_description', $this->website_description);
$websiteTemplateService->clearWebsiteVarsCache();
$this->loadSettings();
$this->dispatch(
'notification',
target: $this->targetNotify,
type: 'success',
message: 'Se han guardado los cambios en las configuraciones.'
);
}
public function render()
{
return view('admin::livewire.website-settings.website-settings');
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace Modules\Admin\App\Models;
use Modules\Admin\App\Models\Sat\CodigoPostal;
use Modules\Admin\App\Models\Sat\Colonia;
use Modules\Admin\App\Models\Sat\Estado;
use Modules\Admin\App\Models\Sat\Municipio;
use Illuminate\Database\Eloquent\Model;
class ContactableAddress extends Model
{
protected $table = 'contactable_addresses';
protected $fillable = [
'contactable_id',
'contactable_type',
'type',
'c_estado',
'c_municipio',
'c_codigo_postal',
'c_colonia',
'direccion',
'num_ext',
'num_int',
'referencia',
'lat',
'lng',
'preference_level',
];
/**
* Casts for the model attributes.
*/
protected $casts = [
'lat' => 'float',
'lng' => 'float',
'preference_level' => 'integer',
];
/**
* Polymorphic relationship to the parent model.
*/
public function contactable()
{
return $this->morphTo();
}
/**
* Relationships to SAT tables.
*/
public function estado()
{
return $this->belongsTo(Estado::class, 'c_estado', 'c_estado');
}
public function municipio()
{
return $this->belongsTo(Municipio::class, 'c_municipio', 'c_municipio');
}
public function codigoPostal()
{
return $this->belongsTo(CodigoPostal::class, 'c_codigo_postal', 'c_codigo_postal');
}
public function colonia()
{
return $this->belongsTo(Colonia::class, 'c_colonia', 'c_colonia');
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace Modules\Admin\App\Models;
use Illuminate\Database\Eloquent\Model;
class ContactableItem extends Model
{
protected $table = 'contactable_items';
protected $fillable = [
'contactable_id',
'contactable_type',
'type',
'data_contact',
'is_preferred',
'preference_level',
];
/**
* Casts for the model attributes.
*/
protected $casts = [
'is_preferred' => 'boolean',
'preference_level' => 'integer',
];
/**
* Polymorphic relationship to the parent model.
*/
public function contactable()
{
return $this->morphTo();
}
}

View File

@ -0,0 +1,62 @@
<?php
namespace Modules\Admin\App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class MediaItem extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'url',
'imageable_type',
'imageable_id',
'type',
'sub_type',
'url',
'path',
'title',
'description',
'order',
];
// the list of types values that can be stored in table
const TYPE_CARD = 1;
const TYPE_BANNER = 2;
const TYPE_COVER = 3;
const TYPE_GALLERY = 4;
const TYPE_BANNER_HOME = 5;
const TYPE_CARD2 = 6;
const TYPE_BANNER2 = 7;
const TYPE_COVER2 = 8;
/**
* List of names for each types.
* @var array
*/
public static $typesList = [
self::TYPE_CARD => 'Card',
self::TYPE_BANNER => 'Banner',
self::TYPE_COVER => 'Cover',
self::TYPE_GALLERY => 'Gallery',
self::TYPE_BANNER_HOME => 'Banner Home',
self::TYPE_CARD2 => 'Card 2',
self::TYPE_BANNER2 => 'Banner 2',
self::TYPE_COVER2 => 'Cover 2',
];
/**
* Get the parent imageable model (user or post).
*/
public function imageable()
{
return $this->morphTo();
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace Modules\Admin\App\Models\Sat;
use Illuminate\Database\Eloquent\Model;
class Banco extends Model
{
// the list of status values that can be stored in table
const STATUS_ENABLED = 10;
const STATUS_DISABLED = 1;
/**
* List of names for each status.
* @var array
*/
public static $statusList = [
self::STATUS_ENABLED => 'Habilitado',
self::STATUS_DISABLED => 'Deshabilitado',
];
/**
* List of names for each status.
* @var array
*/
public static $statusListClass = [
self::STATUS_ENABLED => 'success',
self::STATUS_DISABLED => 'warning',
];
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sat_banco';
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'c_banco',
'descripcion',
'razon_social',
'rfc',
'status',
];
}

View File

@ -0,0 +1,33 @@
<?php
namespace Modules\Admin\App\Models\Sat;
use Illuminate\Database\Eloquent\Model;
class ClaveProdServ extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sat_clave_prod_serv';
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'c_clave_prod_serv',
'descripcion',
'incluir_iva_trasladado',
'incluir_ieps_trasladado',
'complemento_que_debe_incluir',
'fecha_inicio_vigencia',
'fecha_fin_vigencia',
'estimulo_franja_fronteriza',
'palabras_similares',
];
}

View File

@ -0,0 +1,42 @@
<?php
namespace Modules\Admin\App\Models\Sat;
use Illuminate\Database\Eloquent\Model;
class ClaveUnidad extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sat_clave_unidad';
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'c_clave_unidad',
'nombre',
'descripcion',
'nota',
'fecha_de_inicio_de_vigencia',
'fecha_de_fin_de_vigencia',
'simbolo',
'is_base',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'fecha_de_inicio_de_vigencia' => 'datetime',
'fecha_de_fin_de_vigencia' => 'datetime',
];
}

View File

@ -0,0 +1,77 @@
<?php
namespace Modules\Admin\App\Models\Sat;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
class CodigoPostal extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sat_codigo_postal';
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'c_codigo_postal',
'c_estado',
'c_municipio',
'c_localidad',
'estimulo_franja_fronteriza',
'fecha_inicio_de_vigencia',
'fecha_fin_de_vigencia',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'fecha_inicio_de_vigencia' => 'datetime',
'fecha_fin_de_vigencia' => 'datetime',
];
/**
* Get the estado associated with the CodigoPostal.
*/
public function estado(): HasOne
{
return $this->hasOne(Estado::class, 'c_estado', 'c_estado');
}
/**
* Get the municipio associated with the CodigoPostal.
*/
public function municipio(): HasOne
{
return $this->hasOne(Municipio::class, 'c_municipio', 'c_municipio')
->where('c_estado', $this->c_estado);
}
/**
* Get the localidad associated with the CodigoPostal.
*/
public function localidad(): HasOne
{
return $this->hasOne(Localidad::class, 'c_estado', 'c_estado')
->where('c_localidad', $this->c_localidad);
}
/**
* Get the localidad associated with the CodigoPostal.
*/
public function colonias(): HasMany
{
return $this->hasMany(Colonia::class, 'c_codigo_postal', 'c_codigo_postal');
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Modules\Admin\App\Models\Sat;
use Illuminate\Database\Eloquent\Model;
class Colonia extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sat_colonia';
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'c_colonia',
'c_codigo_postal',
'nombre_del_asentamiento',
];
public static function selectList($c_codigo_postal, $c_colonia = false)
{
return self::select('c_colonia', 'nombre_del_asentamiento')
->where('c_codigo_postal', $c_codigo_postal)
->when($c_colonia, function ($query) use ($c_colonia) {
$query->where('c_colonia', $c_colonia);
})
->orderBy('nombre_del_asentamiento')
->pluck('nombre_del_asentamiento', 'c_colonia');
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace Modules\Admin\App\Models\Sat;
use Illuminate\Database\Eloquent\Model;
class Deduccion extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sat_deduccion';
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'c_deduccion',
'descripcion',
'fecha_inicio_de_vigencia',
'fecha_fin_de_vigencia',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'fecha_inicio_de_vigencia' => 'datetime',
'fecha_fin_de_vigencia' => 'datetime',
];
}

View File

@ -0,0 +1,47 @@
<?php
namespace Modules\Admin\App\Models\Sat;
use Illuminate\Database\Eloquent\Model;
class Estado extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sat_estado';
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'c_estado',
'c_pais',
'nombre_del_estado',
'fecha_inicio_de_vigencia',
'fecha_fin_de_vigencia',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'fecha_inicio_de_vigencia' => 'datetime',
'fecha_fin_de_vigencia' => 'datetime',
];
public static function selectList($pais = 'MEX')
{
return self::select('c_estado', 'nombre_del_estado')
->where('c_pais', $pais)
->orderBy('nombre_del_estado')
->pluck('nombre_del_estado', 'c_estado');
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace Modules\Admin\App\Models\Sat;
use Illuminate\Database\Eloquent\Model;
class FormaPago extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sat_forma_pago';
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'c_forma_pago',
'descripcion',
'bancarizado',
'numero_de_operacion',
'rfc_del_emisor_de_la_cuenta_ordenante',
'cuenta_ordenante',
'patron_para_cuenta_ordenante',
'rfc_del_emisor_cuenta_de_beneficiario',
'cuenta_de_benenficiario',
'patron_para_cuenta_beneficiaria',
'tipo_cadena_pago',
'banco_emisor_de_la_cuenta_ordenante',
'fecha_inicio_de_vigencia',
'fecha_fin_de_vigencia',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'fecha_inicio_de_vigencia' => 'datetime',
'fecha_fin_de_vigencia' => 'datetime',
];
public static function selectList()
{
return self::selectRaw('c_forma_pago, CONCAT(c_forma_pago, " - ", descripcion) as value')
->pluck('value', 'c_forma_pago');
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Modules\Admin\App\Models\Sat;
class Impuestos
{
// Definición de constantes para c_impuesto
const IMPUESTOS_ISR = 1;
const IMPUESTOS_IVA = 2;
const IMPUESTOS_IEPS = 3;
public static $catalogo = [
self::IMPUESTOS_ISR => 'ISR',
self::IMPUESTOS_IVA => 'IVA',
self::IMPUESTOS_IEPS => 'IEPS',
];
}

View File

@ -0,0 +1,39 @@
<?php
namespace Modules\Admin\App\Models\Sat;
use Illuminate\Database\Eloquent\Model;
class Localidad extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sat_localidad';
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'c_localidad',
'c_estado',
'descripcion',
'fecha_de_inicio_de_vigencia',
'fecha_de_fin_de_vigencia',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'fecha_de_inicio_de_vigencia' => 'datetime',
'fecha_de_fin_de_vigencia' => 'datetime',
];
}

View File

@ -0,0 +1,14 @@
<?php
namespace Modules\Admin\App\Models\Sat;
class MetodoPago
{
const METODO_PAGO_PUE = 'PUE';
const METODO_PAGO_PPD = 'PPD';
public static $catalogo = [
self::METODO_PAGO_PUE => 'Pago en una sola exhibición',
self::METODO_PAGO_PPD => 'Pago en parcialidades o diferido',
];
}

View File

@ -0,0 +1,46 @@
<?php
namespace Modules\Admin\App\Models\Sat;
use Illuminate\Database\Eloquent\Model;
class Moneda extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sat_moneda';
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'c_moneda',
'descripcion',
'decimales',
'porcentaje_variacion',
'fecha_inicio_de_vigencia',
'fecha_fin_de_vigencia',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'fecha_inicio_de_vigencia' => 'datetime',
'fecha_fin_de_vigencia' => 'datetime',
];
public static function selectList()
{
return self::selectRaw('c_moneda, CONCAT_WS(" ", c_moneda, "-", descripcion) as text')
->pluck('text', 'c_moneda');
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace Modules\Admin\App\Models\Sat;
use Illuminate\Database\Eloquent\Model;
class Municipio extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sat_municipio';
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'c_municipio',
'c_estado',
'descripcion',
'fecha_de_inicio_de_vigencia',
'fecha_de_fin_de_vigencia',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'fecha_de_inicio_de_vigencia' => 'datetime',
'fecha_de_fin_de_vigencia' => 'datetime',
];
public static function selectList($c_estado, $c_municipio = false)
{
return self::select('c_municipio', 'descripcion')
->where('c_estado', $c_estado)
->when($c_municipio, function ($query) use ($c_municipio) {
$query->where('c_municipio', $c_municipio);
})
->orderBy('descripcion')
->pluck('descripcion', 'c_municipio');
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Modules\Admin\App\Models\Sat;
class ObjetoImp
{
// Definición de constantes para c_objeto_imp
const OBJETO_IMP_NO_OBJETO = 1;
const OBJETO_IMP_SI_OBJETO = 2;
const OBJETO_IMP_SI_OBJETO_NO_DESGLOSE = 3;
const OBJETO_IMP_SI_OBJETO_NO_CAUSA = 4;
const OBJETO_IMP_SI_OBJETO_IVA_CREDITO = 5;
public static $catalogo = [
self::OBJETO_IMP_NO_OBJETO => 'No objeto de impuesto.',
self::OBJETO_IMP_SI_OBJETO => 'Sí objeto de impuesto.',
self::OBJETO_IMP_SI_OBJETO_NO_DESGLOSE => 'Sí objeto del impuesto y no obligado al desglose.',
self::OBJETO_IMP_SI_OBJETO_NO_CAUSA => 'Sí objeto del impuesto y no causa impuesto.',
self::OBJETO_IMP_SI_OBJETO_IVA_CREDITO => 'Sí objeto del impuesto, IVA crédito PODEBI.',
];
}

View File

@ -0,0 +1,39 @@
<?php
namespace Modules\Admin\App\Models\Sat;
use Illuminate\Database\Eloquent\Model;
class Pais extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sat_pais';
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'c_pais',
'descripcion',
'formato_de_codigo_postal',
'formato_de_registro_de_identidad_tributaria',
'validacion_del_registro_de_identidad_tributaria',
'agrupaciones',
];
public static function selectList()
{
return self::select('c_pais', 'descripcion')
->get()
->mapWithKeys(function ($item) {
return [$item->c_pais => $item->c_pais . ' - ' . $item->descripcion];
});
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace Modules\Admin\App\Models\Sat;
use Illuminate\Database\Eloquent\Model;
class Percepcion extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sat_percepcion';
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'c_percepcion',
'descripcion',
'fecha_inicio_de_vigencia',
'fecha_fin_de_vigencia',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'fecha_inicio_de_vigencia' => 'datetime',
'fecha_fin_de_vigencia' => 'datetime',
];
}

View File

@ -0,0 +1,21 @@
<?php
namespace Modules\Admin\App\Models\Sat;
class Periodicidad
{
// Definición de constantes para c_periodicidad
const PERIODICIDAD_DIARIO = 1;
const PERIODICIDAD_SEMANAL = 2;
const PERIODICIDAD_QUINCENAL = 3;
const PERIODICIDAD_MENSUAL = 4;
const PERIODICIDAD_BIMESTRAL = 5;
public static $catalogo = [
self::PERIODICIDAD_DIARIO => 'Diario',
self::PERIODICIDAD_SEMANAL => 'Semanal',
self::PERIODICIDAD_QUINCENAL => 'Quincenal',
self::PERIODICIDAD_MENSUAL => 'Mensual',
self::PERIODICIDAD_BIMESTRAL => 'Bimestral',
];
}

View File

@ -0,0 +1,33 @@
<?php
namespace Modules\Admin\App\Models\Sat;
class PeriodicidadPago
{
// Definición de constantes para Tipos de Periodicidad de Pago
const PERIODICIDAD_DIARIO = 1;
const PERIODICIDAD_SEMANAL = 2;
const PERIODICIDAD_CATORCENAL = 3;
const PERIODICIDAD_QUINCENAL = 4;
const PERIODICIDAD_MENSUAL = 5;
const PERIODICIDAD_BIMESTRAL = 6;
const PERIODICIDAD_UNIDAD_OBRA = 7;
const PERIODICIDAD_COMISION = 8;
const PERIODICIDAD_PRECIO_ALZADO = 9;
const PERIODICIDAD_DECENAL = 10;
const PERIODICIDAD_OTRA = 99;
public static $tipoPeriodicidad = [
self::PERIODICIDAD_DIARIO => 'Diario',
self::PERIODICIDAD_SEMANAL => 'Semanal',
self::PERIODICIDAD_CATORCENAL => 'Catorcenal',
self::PERIODICIDAD_QUINCENAL => 'Quincenal',
self::PERIODICIDAD_MENSUAL => 'Mensual',
self::PERIODICIDAD_BIMESTRAL => 'Bimestral',
self::PERIODICIDAD_UNIDAD_OBRA => 'Unidad obra',
self::PERIODICIDAD_COMISION => 'Comisión',
self::PERIODICIDAD_PRECIO_ALZADO => 'Precio alzado',
self::PERIODICIDAD_DECENAL => 'Decenal',
self::PERIODICIDAD_OTRA => 'Otra Periodicidad'
];
}

View File

@ -0,0 +1,46 @@
<?php
namespace Modules\Admin\App\Models\Sat;
use Illuminate\Database\Eloquent\Model;
class RegimenFiscal extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sat_regimen_fiscal';
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'c_regimen_fiscal',
'descripcion',
'fisica',
'moral',
'fecha_de_inicio_de_vigencia',
'fecha_de_fin_de_vigencia',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'fecha_de_inicio_de_vigencia' => 'datetime',
'fecha_de_fin_de_vigencia' => 'datetime',
];
public static function selectList()
{
return self::selectRaw('c_regimen_fiscal, CONCAT(c_regimen_fiscal, " - ", descripcion) as value')
->pluck('value', 'c_regimen_fiscal');
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Modules\Admin\App\Models\Sat;
class TipoComprobante
{
// Definición de constantes para c_tipo_de_comprobante
const TIPO_COMPROBANTE_INGRESO = 'I';
const TIPO_COMPROBANTE_EGRESO = 'E';
const TIPO_COMPROBANTE_TRASLADO = 'T';
const TIPO_COMPROBANTE_NOMINA = 'N';
const TIPO_COMPROBANTE_PAGO = 'P';
public static $catalogo = [
self::TIPO_COMPROBANTE_INGRESO => 'Ingreso',
self::TIPO_COMPROBANTE_EGRESO => 'Egreso',
self::TIPO_COMPROBANTE_TRASLADO => 'Traslado',
self::TIPO_COMPROBANTE_NOMINA => 'Nómina',
self::TIPO_COMPROBANTE_PAGO => 'Pago',
];
}

View File

@ -0,0 +1,17 @@
<?php
namespace Modules\Admin\App\Models\Sat;
class TipoFactor
{
// Definición de constantes para c_tipo_factor
const TIPO_FACTOR_TASA = 1;
const TIPO_FACTOR_CUOTA = 2;
const TIPO_FACTOR_EXENTO = 3;
public static $catalogo = [
self::TIPO_FACTOR_TASA => 'Tasa',
self::TIPO_FACTOR_CUOTA => 'Cuota',
self::TIPO_FACTOR_EXENTO => 'Exento',
];
}

View File

@ -0,0 +1,25 @@
<?php
namespace Modules\Admin\App\Models\Sat;
class TipoRelacion
{
// Definición de constantes para c_tipo_relacion
const TIPO_RELACION_NOTA_CREDITO = 1;
const TIPO_RELACION_NOTA_DEBITO = 2;
const TIPO_RELACION_DEVOLUCION_MERCANCIA = 3;
const TIPO_RELACION_SUSTITUCION_CFDI = 4;
const TIPO_RELACION_TRASLADOS_FACTURADOS = 5;
const TIPO_RELACION_FACTURA_TRASLADOS = 6;
const TIPO_RELACION_CFDI_ANTICIPO = 7;
public static $catalogo = [
self::TIPO_RELACION_NOTA_CREDITO => 'Nota de crédito de los documentos relacionados',
self::TIPO_RELACION_NOTA_DEBITO => 'Nota de débito de los documentos relacionados',
self::TIPO_RELACION_DEVOLUCION_MERCANCIA => 'Devolución de mercancía sobre facturas o traslados previos',
self::TIPO_RELACION_SUSTITUCION_CFDI => 'Sustitución de los CFDI previos',
self::TIPO_RELACION_TRASLADOS_FACTURADOS => 'Traslados de mercancías facturados previamente',
self::TIPO_RELACION_FACTURA_TRASLADOS => 'Factura generada por los traslados previos',
self::TIPO_RELACION_CFDI_ANTICIPO => 'CFDI por aplicación de anticipo',
];
}

View File

@ -0,0 +1,47 @@
<?php
namespace Modules\Admin\App\Models\Sat;
use Illuminate\Database\Eloquent\Model;
class UsoCfdi extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sat_uso_cfdi';
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'c_uso_cfdi',
'descripcion',
'aplica_para_tipo_persona_fisica',
'aplica_para_tipo_persona_moral',
'fecha_inicio_de_vigencia',
'fecha_fin_de_vigencia',
'regimen_fiscal_receptor',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'fecha_inicio_de_vigencia' => 'datetime',
'fecha_fin_de_vigencia' => 'datetime',
];
public static function selectList()
{
return self::selectRaw('c_uso_cfdi, CONCAT(c_uso_cfdi, " - ", descripcion) as value')
->pluck('value', 'c_uso_cfdi');
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Modules\Admin\App\Models;
use Illuminate\Database\Eloquent\Model;
class Setting extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'key',
'value',
'user_id',
];
public $timestamps = false;
// Relación con el usuario
public function user()
{
return $this->belongsTo(User::class);
}
// Scope para obtener configuraciones de un usuario específico
public function scopeForUser($query, $userId)
{
return $query->where('user_id', $userId);
}
// Configuraciones globales (sin usuario)
public function scopeGlobal($query)
{
return $query->whereNull('user_id');
}
}

View File

@ -0,0 +1,392 @@
<?php
namespace Modules\Admin\App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Http\UploadedFile;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\ImageManager;
use Intervention\Image\Typography\FontFactory;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Sanctum\HasApiTokens;
use OwenIt\Auditing\Contracts\Auditable as AuditableContract;
use OwenIt\Auditing\Auditable;
use Spatie\Permission\Traits\HasRoles;
use Modules\Admin\App\Models\Sat\CodigoPostal;
use Modules\Admin\App\Models\Sat\RegimenFiscal;
use Modules\Admin\App\Models\Sat\UsoCfdi;
use Modules\Admin\App\Notifications\CustomResetPasswordNotification;
class User extends Authenticatable implements MustVerifyEmail, AuditableContract
{
use HasRoles,
HasApiTokens,
HasFactory,
Notifiable,
TwoFactorAuthenticatable,
Auditable;
// the list of status values that can be stored in table
const STATUS_ENABLED = 10;
const STATUS_DISABLED = 1;
const STATUS_REMOVED = 0;
const AVATAR_DISK = 'public';
const PROFILE_PHOTO_DIR = 'profile-photos/';
const INITIAL_AVATAR_DIR = 'initial-avatars/';
const INITIAL_MAX_LENGTH = 4;
const AVATAR_WIDTH = 512;
const AVATAR_HEIGHT = 512;
const AVATAR_BACKGROUND = '#EBF4FF'; // Fondo por defecto
const AVATAR_COLORS = [
'#7367f0',
'#808390',
'#28c76f',
'#ff4c51',
'#ff9f43',
'#00bad1',
'#4b4b4b',
];
/**
* List of names for each status.
* @var array
*/
public static $statusList = [
self::STATUS_ENABLED => 'Habilitado',
self::STATUS_DISABLED => 'Deshabilitado',
self::STATUS_REMOVED => 'Eliminado',
];
/**
* List of names for each status.
* @var array
*/
public static $statusListClass = [
self::STATUS_ENABLED => 'success',
self::STATUS_DISABLED => 'warning',
self::STATUS_REMOVED => 'danger',
];
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'contact_code',
'name',
'last_name',
'email',
'password',
'profile_photo_path',
'company',
'birth_date',
'hire_date',
'curp',
'nss',
'job_title',
'face_vector',
'rfc',
'nombre_fiscal',
'tipo_persona',
'c_regimen_fiscal',
'domicilio_fiscal',
'c_uso_cfdi',
'is_partner',
'is_employee',
'is_prospect',
'is_customer',
'is_provider',
'is_user',
'status',
'created_by',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
'two_factor_recovery_codes',
'two_factor_secret',
];
/**
* The accessors to append to the model's array form.
*
* @var array<int, string>
*/
protected $appends = [
'profile_photo_url',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'face_vector' => 'array',
'birth_date' => 'date',
'hire_date' => 'date',
];
}
/**
* Attributes to include in the Audit.
*
* @var array
*/
protected $auditInclude = [
'name',
'email',
];
public function updateProfilePhoto(UploadedFile $image_avatar)
{
try {
// Verificar si el archivo existe
if (!file_exists($image_avatar->getRealPath()))
throw new \Exception('El archivo no existe en la ruta especificada.');
if (!in_array($image_avatar->getClientOriginalExtension(), ['jpg', 'jpeg', 'png']))
throw new \Exception('El formato del archivo debe ser JPG o PNG.');
// Directorio donde se guardarán los avatares
$avatarDisk = self::AVATAR_DISK;
$avatarPath = self::PROFILE_PHOTO_DIR;
$avatarName = uniqid('avatar_') . '.png'; // Nombre único para el avatar
// Crear la instancia de ImageManager
$driver = config('image.driver', 'gd');
$manager = new ImageManager($driver);
// Crear el directorio si no existe
if (!Storage::disk($avatarDisk)->exists($avatarPath))
Storage::disk($avatarDisk)->makeDirectory($avatarPath);
// Leer la imagen
$image = $manager->read($image_avatar->getRealPath());
// crop the best fitting 5:3 (600x360) ratio and resize to 600x360 pixel
$image->cover(self::AVATAR_WIDTH, self::AVATAR_HEIGHT);
// Guardar la imagen en el disco de almacenamiento gestionado por Laravel
Storage::disk($avatarDisk)->put($avatarPath . $avatarName, $image->toPng(indexed: true));
// Elimina el avatar existente si hay uno
$this->deleteProfilePhoto();
// Update the user's profile photo path
$this->forceFill([
'profile_photo_path' => $avatarName,
])->save();
} catch (\Exception $e) {
throw new \Exception('Ocurrió un error al actualizar el avatar. ' . $e->getMessage());
}
}
public function deleteProfilePhoto()
{
if (!empty($this->profile_photo_path)) {
$avatarDisk = self::AVATAR_DISK;
Storage::disk($avatarDisk)->delete($this->profile_photo_path);
$this->forceFill([
'profile_photo_path' => null,
])->save();
}
}
public function getAvatarColor()
{
// Selecciona un color basado en el id del usuario
return self::AVATAR_COLORS[$this->id % count(self::AVATAR_COLORS)];
}
public static function getAvatarImage($name, $color, $background, $size)
{
$avatarDisk = self::AVATAR_DISK;
$directory = self::INITIAL_AVATAR_DIR;
$initials = self::getInitials($name);
$cacheKey = "avatar-{$initials}-{$color}-{$background}-{$size}";
$path = "{$directory}/{$cacheKey}.png";
$storagePath = storage_path("app/public/{$path}");
// Verificar si el avatar ya está en caché
if (Storage::disk($avatarDisk)->exists($path))
return response()->file($storagePath);
// Crear el avatar
$image = self::createAvatarImage($name, $color, $background, $size);
// Guardar en el directorio de iniciales
Storage::disk($avatarDisk)->put($path, $image->toPng(indexed: true));
// Retornar la imagen directamente
return response()->file($storagePath);
}
private static function createAvatarImage($name, $color, $background, $size)
{
// Usar la configuración del driver de imagen
$driver = config('image.driver', 'gd');
$manager = new ImageManager($driver);
$initials = self::getInitials($name);
// Crear la imagen con fondo
$image = $manager->create($size, $size)
->fill($background);
// Escribir texto en la imagen
$image->text(
$initials,
$size / 2, // Centrar horizontalmente
$size / 2, // Centrar verticalmente
function (FontFactory $font) use ($color, $size) {
$font->file(base_path('/modules/Admin/Resources/assets/vendor/fonts/OpenSans/static/OpenSans-Bold.ttf'));
$font->size($size * 0.4);
$font->color($color);
$font->align('center');
$font->valign('middle');
}
);
return $image;
}
public static function getInitials($name)
{
// Manejar casos de nombres vacíos o nulos
if (empty($name))
return 'NA';
// Usar array_map para mayor eficiencia
$initials = implode('', array_map(function ($word) {
return mb_substr($word, 0, 1);
}, explode(' ', $name)));
$initials = substr($initials, 0, self::INITIAL_MAX_LENGTH);
return strtoupper($initials);
}
public function getProfilePhotoUrlAttribute()
{
if ($this->profile_photo_path)
return Storage::url(self::PROFILE_PHOTO_DIR . '/' . $this->profile_photo_path);
// Generar URL del avatar por iniciales
$name = urlencode($this->fullname);
$color = ltrim($this->getAvatarColor(), '#');
$background = ltrim(self::AVATAR_BACKGROUND, '#');
$size = (self::AVATAR_WIDTH + self::AVATAR_HEIGHT) / 2;
return url("/admin/usuario/avatar?name={$name}&color={$color}&background={$background}&size={$size}");
}
public function getFullnameAttribute()
{
return trim($this->name . ' ' . $this->last_name);
}
public function getInitialsAttribute()
{
return self::getInitials($this->fullname);
}
/**
* Envía la notificación de restablecimiento de contraseña.
*
* @param string $token
*/
public function sendPasswordResetNotification($token)
{
// Usar la notificación personalizada
$this->notify(new CustomResetPasswordNotification($token));
}
/**
* Relations
*/
// User who created this user
public function creator()
{
return $this->belongsTo(self::class, 'created_by');
}
// Regimen fiscal
public function regimenFiscal()
{
return $this->belongsTo(RegimenFiscal::class, 'c_regimen_fiscal', 'c_regimen_fiscal');
}
// Domicilio fiscal
public function domicilioFiscal()
{
return $this->belongsTo(CodigoPostal::class, 'domicilio_fiscal', 'c_codigo_postal');
}
// Uso de CFDI
public function usoCfdi()
{
return $this->belongsTo(UsoCfdi::class, 'c_uso_cfdi', 'c_uso_cfdi');
}
/**
* Helper methods
*/
public function isActive()
{
return $this->status === 1;
}
public function isPartner()
{
return $this->is_partner === 1;
}
public function isEmployee()
{
return $this->is_employee === 1;
}
public function isCustomer()
{
return $this->is_customer === 1;
}
public function isProvider()
{
return $this->is_provider === 1;
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace Modules\Admin\App\Models;
use Illuminate\Database\Eloquent\Model;
class UserLogin extends Model
{
protected $fillable = [
'user_id',
'ip_address',
'user_agent'
];
}

View File

@ -0,0 +1,117 @@
<?php
namespace Modules\Admin\App\Notifications;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Log;
use Modules\Admin\App\Models\Setting;
class CustomResetPasswordNotification extends Notification
{
use Queueable;
public $token;
/**
* Crea una nueva instancia de notificación.
*/
public function __construct($token)
{
$this->token = $token;
}
/**
* Configura el canal de la notificación.
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Configura el mensaje de correo.
*/
public function toMail($notifiable)
{
try {
// Cargar configuración SMTP desde la base de datos
$this->loadDynamicMailConfig();
$resetUrl = url(route('password.reset', [
'token' => $this->token,
'email' => $notifiable->getEmailForPasswordReset()
], false));
$appTitle = Setting::global()->where('key', 'website_title')->first()->value ?? Config::get('_var.appTitle');
$imageBase64 = 'data:image/png;base64,' . base64_encode(file_get_contents(public_path('/assets/img/logo/koneko-04.png')));
$expireMinutes = Config::get('auth.passwords.' . Config::get('auth.defaults.passwords') . '.expire', 60);
Config::set('app.name', $appTitle);
return (new MailMessage)
->subject("Restablece tu contraseña - {$appTitle}")
->markdown('admin::notifications.email', [ // Usar tu plantilla del módulo
'greeting' => "Hola {$notifiable->name}",
'introLines' => [
'Estás recibiendo este correo porque solicitaste restablecer tu contraseña.',
],
'actionText' => 'Restablecer contraseña',
'actionUrl' => $resetUrl,
'outroLines' => [
"Este enlace expirará en {$expireMinutes} minutos.",
'Si no solicitaste este cambio, no se requiere realizar ninguna acción.',
],
'displayableActionUrl' => $resetUrl, // Para el subcopy
'image' => $imageBase64, // Imagen del logo
]);
/*
*/
} catch (\Exception $e) {
// Registrar el error
Log::error('Error al enviar el correo de restablecimiento: ' . $e->getMessage());
// Retornar un mensaje alternativo
return (new MailMessage)
->subject('Restablece tu contraseña')
->line('Ocurrió un error al enviar el correo. Por favor, intenta de nuevo más tarde.');
}
}
/**
* Cargar configuración SMTP desde la base de datos.
*/
protected function loadDynamicMailConfig()
{
try {
$smtpConfig = Setting::where('key', 'LIKE', 'mail_%')
->pluck('value', 'key');
if ($smtpConfig->isEmpty()) {
throw new Exception('No SMTP configuration found in the database.');
}
Config::set('mail.mailers.smtp.host', $smtpConfig['mail_mailers_smtp_host'] ?? null);
Config::set('mail.mailers.smtp.port', $smtpConfig['mail_mailers_smtp_port'] ?? null);
Config::set('mail.mailers.smtp.username', $smtpConfig['mail_mailers_smtp_username'] ?? null);
Config::set(
'mail.mailers.smtp.password',
isset($smtpConfig['mail_mailers_smtp_password'])
? Crypt::decryptString($smtpConfig['mail_mailers_smtp_password'])
: null
);
Config::set('mail.mailers.smtp.encryption', $smtpConfig['mail_mailers_smtp_encryption'] ?? null);
Config::set('mail.from.address', $smtpConfig['mail_from_address'] ?? null);
Config::set('mail.from.name', $smtpConfig['mail_from_name'] ?? null);
} catch (Exception $e) {
Log::error('SMTP Configuration Error: ' . $e->getMessage());
// Opcional: Puedes lanzar la excepción o manejarla de otra manera.
throw new Exception('Error al cargar la configuración SMTP.');
}
}
}

View File

@ -0,0 +1,124 @@
<?php
namespace Modules\Admin\App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Laravel\Fortify\Fortify;
use Modules\Admin\App\Actions\Fortify\CreateNewUser;
use Modules\Admin\App\Actions\Fortify\ResetUserPassword;
use Modules\Admin\App\Actions\Fortify\UpdateUserPassword;
use Modules\Admin\App\Actions\Fortify\UpdateUserProfileInformation;
use Modules\Admin\App\Models\User;
use Modules\Admin\App\Services\AdminTemplateService;
class FortifyServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Fortify::createUsersUsing(CreateNewUser::class);
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
RateLimiter::for('login', function (Request $request) {
$throttleKey = Str::transliterate(Str::lower($request->input(Fortify::username())) . '|' . $request->ip());
return Limit::perMinute(5)->by($throttleKey);
});
RateLimiter::for('two-factor', function (Request $request) {
return Limit::perMinute(5)->by($request->session()->get('login.id'));
});
Fortify::authenticateUsing(function (Request $request) {
$user = User::where('email', $request->email)
->where('status', User::STATUS_ENABLED)
->first();
if ($user && Hash::check($request->password, $user->password)) {
return $user;
}
});
// Simula lo que hace tu middleware y comparte `_admin`
$viewMode = Config::get('custom.custom.authViewMode');
$adminVars = app(AdminTemplateService::class)->getAdminVars();
// Configurar la vista del login
Fortify::loginView(function () use ($viewMode, $adminVars) {
$pageConfigs = ['myLayout' => 'blank'];
view()->share('_admin', $adminVars);
return view("admin::auth.login-{$viewMode}", ['pageConfigs' => $pageConfigs]);
});
// Configurar la vista del registro (si lo necesitas)
Fortify::registerView(function () use ($viewMode, $adminVars) {
$pageConfigs = ['myLayout' => 'blank'];
view()->share('_admin', $adminVars);
return view("admin::auth.register-{$viewMode}", ['pageConfigs' => $pageConfigs]);
});
// Configurar la vista de restablecimiento de contraseñas
Fortify::requestPasswordResetLinkView(function () use ($viewMode, $adminVars) {
$pageConfigs = ['myLayout' => 'blank'];
view()->share('_admin', $adminVars);
return view("admin::auth.forgot-password-{$viewMode}", ['pageConfigs' => $pageConfigs]);
});
Fortify::resetPasswordView(function ($request) use ($viewMode, $adminVars) {
$pageConfigs = ['myLayout' => 'blank'];
view()->share('_admin', $adminVars);
return view("admin::auth.reset-password-{$viewMode}", ['pageConfigs' => $pageConfigs, 'request' => $request]);
});
// Vista de verificación de correo electrónico
Fortify::verifyEmailView(function () use ($viewMode, $adminVars) {
view()->share('_admin', $adminVars);
return view("admin::auth.verify-email-{$viewMode}");
});
// Vista de confirmación de contraseña
Fortify::confirmPasswordView(function () use ($viewMode, $adminVars) {
$pageConfigs = ['myLayout' => 'blank'];
view()->share('_admin', $adminVars);
return view("admin::auth.confirm-password-{$viewMode}", ['pageConfigs' => $pageConfigs]);
});
// Configurar la vista para la verificación de dos factores
Fortify::twoFactorChallengeView(function () use ($viewMode, $adminVars) {
$pageConfigs = ['myLayout' => 'blank'];
view()->share('_admin', $adminVars);
return view("admin::auth.two-factor-challenge-{$viewMode}", ['pageConfigs' => $pageConfigs]);
});
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Modules\Admin\App\Providers;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\ServiceProvider;
use Modules\Admin\App\Services\GlobalSettingsService;
class GlobalSettingsProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
$this->mergeConfigFrom(__DIR__ . '/../../Config/_var.php', '_var');
$this->mergeConfigFrom(__DIR__ . '/../../Config/custom.php', 'custom');
}
/**
* Bootstrap services.
*/
public function boot(GlobalSettingsService $globalSettingsService): void
{
$globalSettingsService->loadSystemConfig();
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Modules\Admin\App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\View;
use Illuminate\Foundation\AliasLoader;
use Modules\Admin\App\Console\Commands\CleanInitialAvatars;
use Modules\Admin\App\Http\View\Composers\VuexyTemplateComposer;
class VuexyAdminProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// Register the module's aliases
AliasLoader::getInstance()->alias('Helper', \Modules\Admin\App\Helpers\Helpers::class);
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
// Register any module-specific event listeners here
if ($this->app->runningInConsole()) {
$this->commands([
CleanInitialAvatars::class,
]);
return;
}
// Composer para las vistas del módulo Admin
View::composer('admin::layouts.vuexy.*', VuexyTemplateComposer::class);
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace Modules\Admin\App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class NotEmptyHtml implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
// Eliminar etiquetas HTML y espacios en blanco
$strippedContent = trim(strip_tags($value));
// Considerar vacío si no queda contenido significativo
if (empty($strippedContent)) {
$fail('El contenido no puede estar vacío.');
}
}
}

View File

@ -0,0 +1,215 @@
<?php
namespace Modules\Admin\App\Services;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\ImageManager;
use Modules\Admin\App\Models\Setting;
class AdminSettingsService
{
private $driver;
private $imageDisk = 'public';
private $favicon_basePath = 'favicon/';
private $image_logo_basePath = 'images/logo/';
private $faviconsSizes = [
'180x180' => [180, 180],
'192x192' => [192, 192],
'152x152' => [152, 152],
'120x120' => [120, 120],
'76x76' => [76, 76],
'16x16' => [16, 16],
];
private $imageLogoMaxPixels1 = 22500; // Primera versión (px^2)
private $imageLogoMaxPixels2 = 75625; // Segunda versión (px^2)
private $imageLogoMaxPixels3 = 262144; // Tercera versión (px^2)
private $imageLogoMaxPixels4 = 230400; // Tercera versión (px^2) en Base64
protected $cacheTTL = 60 * 24 * 30; // 30 días en minutos
public function __construct()
{
$this->driver = config('image.driver', 'gd');
}
public function updateSetting(string $key, string $value): bool
{
$setting = Setting::updateOrCreate(
['key' => $key],
['value' => trim($value)]
);
return $setting->save();
}
public function processAndSaveFavicon($image): void
{
Storage::makeDirectory($this->imageDisk . '/' . $this->favicon_basePath);
// Eliminar favicons antiguos
$this->deleteOldFavicons();
// Guardar imagen original
$imageManager = new ImageManager($this->driver);
$imageName = uniqid('admin_favicon_');
$image = $imageManager->read($image->getRealPath());
foreach ($this->faviconsSizes as $size => [$width, $height]) {
$resizedPath = $this->favicon_basePath . $imageName . "_{$size}.png";
$image->cover($width, $height);
Storage::disk($this->imageDisk)->put($resizedPath, $image->toPng(indexed: true));
}
$this->updateSetting('admin_favicon_ns', $this->favicon_basePath . $imageName);
}
protected function deleteOldFavicons(): void
{
// Obtener el favicon actual desde la base de datos
$currentFavicon = Setting::where('key', 'admin_favicon_ns')->value('value');
if ($currentFavicon) {
$filePaths = [
$this->imageDisk . '/' . $currentFavicon,
$this->imageDisk . '/' . $currentFavicon . '_16x16.png',
$this->imageDisk . '/' . $currentFavicon . '_76x76.png',
$this->imageDisk . '/' . $currentFavicon . '_120x120.png',
$this->imageDisk . '/' . $currentFavicon . '_152x152.png',
$this->imageDisk . '/' . $currentFavicon . '_180x180.png',
$this->imageDisk . '/' . $currentFavicon . '_192x192.png',
];
foreach ($filePaths as $filePath) {
if (Storage::exists($filePath)) {
Storage::delete($filePath);
}
}
}
}
public function processAndSaveImageLogo($image, string $type = ''): void
{
// Crear directorio si no existe
Storage::makeDirectory($this->imageDisk . '/' . $this->image_logo_basePath);
// Eliminar imágenes antiguas
$this->deleteOldImageWebapp($type);
// Leer imagen original
$imageManager = new ImageManager($this->driver);
$image = $imageManager->read($image->getRealPath());
// Generar tres versiones con diferentes áreas máximas
$this->generateAndSaveImage($image, $type, $this->imageLogoMaxPixels1, 'small'); // Versión 1
$this->generateAndSaveImage($image, $type, $this->imageLogoMaxPixels2, 'medium'); // Versión 2
$this->generateAndSaveImage($image, $type, $this->imageLogoMaxPixels3); // Versión 3
$this->generateAndSaveImageAsBase64($image, $type, $this->imageLogoMaxPixels4); // Versión 3
}
private function generateAndSaveImage($image, string $type, int $maxPixels, string $suffix = ''): void
{
$imageClone = clone $image;
// Escalar imagen conservando aspecto
$this->resizeImageToMaxPixels($imageClone, $maxPixels);
$imageName = 'admin_image_logo' . ($suffix ? '_' . $suffix : '') . ($type == 'dark' ? '_dark' : '');
// Generar nombre y ruta
$imageNameUid = uniqid($imageName . '_', ".png");
$resizedPath = $this->image_logo_basePath . $imageNameUid;
// Guardar imagen en PNG
Storage::disk($this->imageDisk)->put($resizedPath, $imageClone->toPng(indexed: true));
// Actualizar configuración
$this->updateSetting($imageName, $resizedPath);
}
private function resizeImageToMaxPixels($image, int $maxPixels)
{
// Obtener dimensiones originales de la imagen
$originalWidth = $image->width(); // Método para obtener el ancho
$originalHeight = $image->height(); // Método para obtener el alto
// Calcular el aspecto
$aspectRatio = $originalWidth / $originalHeight;
// Calcular dimensiones redimensionadas conservando aspecto
if ($aspectRatio > 1) { // Ancho es dominante
$newWidth = sqrt($maxPixels * $aspectRatio);
$newHeight = $newWidth / $aspectRatio;
} else { // Alto es dominante
$newHeight = sqrt($maxPixels / $aspectRatio);
$newWidth = $newHeight * $aspectRatio;
}
// Redimensionar la imagen
$image->resize(
round($newWidth), // Redondear para evitar problemas con números decimales
round($newHeight),
function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
}
);
return $image;
}
private function generateAndSaveImageAsBase64($image, string $type, int $maxPixels): void
{
$imageClone = clone $image;
// Redimensionar imagen conservando el aspecto
$this->resizeImageToMaxPixels($imageClone, $maxPixels);
// Convertir a Base64
$base64Image = (string) $imageClone->toJpg(40)->toDataUri();
// Guardar como configuración
$this->updateSetting(
"admin_image_logo_base64" . ($type === 'dark' ? '_dark' : ''),
$base64Image // Ya incluye "data:image/png;base64,"
);
}
protected function deleteOldImageWebapp(string $type = ''): void
{
// Determinar prefijo según el tipo (normal o dark)
$suffix = $type === 'dark' ? '_dark' : '';
// Claves relacionadas con las imágenes que queremos limpiar
$imageKeys = [
"admin_image_logo{$suffix}",
"admin_image_logo_small{$suffix}",
"admin_image_logo_medium{$suffix}",
];
// Recuperar las imágenes actuales en una sola consulta
$settings = Setting::whereIn('key', $imageKeys)->pluck('value', 'key');
foreach ($imageKeys as $key) {
// Obtener la imagen correspondiente
$currentImage = $settings[$key] ?? null;
if ($currentImage) {
// Construir la ruta del archivo y eliminarlo si existe
$filePath = $this->imageDisk . '/' . $currentImage;
if (Storage::exists($filePath)) {
Storage::delete($filePath);
}
// Eliminar la configuración de la base de datos
Setting::where('key', $key)->delete();
}
}
}
}

View File

@ -0,0 +1,156 @@
<?php
namespace Modules\Admin\App\Services;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Schema;
use Modules\Admin\App\Models\Setting;
class AdminTemplateService
{
protected $cacheTTL = 60 * 24 * 30; // 30 días en minutos
public function updateSetting(string $key, string $value): bool
{
$setting = Setting::updateOrCreate(
['key' => $key],
['value' => trim($value)]
);
return $setting->save();
}
public function getAdminVars($adminSetting = false): array
{
try {
// Verificar si el sistema está inicializado (la tabla `migrations` existe)
if (!Schema::hasTable('migrations')) {
return $this->getDefaultAdminVars($adminSetting);
}
// Cargar desde el caché o la base de datos si está disponible
return Cache::remember('admin_settings', $this->cacheTTL, function () use ($adminSetting) {
$settings = Setting::global()
->where('key', 'LIKE', 'admin_%')
->pluck('value', 'key')
->toArray();
$adminSettings = $this->buildAdminVarsArray($settings);
return $adminSetting
? $adminSettings[$adminSetting]
: $adminSettings;
});
} catch (\Exception $e) {
// En caso de error, devolver valores predeterminados
return $this->getDefaultAdminVars($adminSetting);
}
}
private function getDefaultAdminVars($adminSetting = false): array
{
$defaultSettings = [
'title' => config('_var.appTitle', 'Default Title'),
'author' => config('_var.author', 'Default Author'),
'description' => config('_var.description', 'Default Description'),
'favicon' => $this->getFaviconPaths([]),
'app_name' => config('_var.appName', 'Default App Name'),
'image_logo' => $this->getImageLogoPaths([]),
];
return $adminSetting
? $defaultSettings[$adminSetting] ?? null
: $defaultSettings;
}
private function buildAdminVarsArray(array $settings): array
{
return [
'title' => $settings['admin_title'] ?? config('_var.appTitle'),
'author' => config('_var.author'),
'description' => config('_var.description'),
'favicon' => $this->getFaviconPaths($settings),
'app_name' => $settings['admin_app_name'] ?? config('_var.appName'),
'image_logo' => $this->getImageLogoPaths($settings),
];
}
public function getVuexyCustomizerVars()
{
// Obtener valores de la base de datos
$settings = Setting::global()
->where('key', 'LIKE', 'vuexy_%')
->pluck('value', 'key')
->toArray();
// Obtener configuraciones predeterminadas
$defaultConfig = Config::get('custom.custom', []);
// Mezclar las configuraciones predeterminadas con las de la base de datos
return collect($defaultConfig)
->mapWithKeys(function ($defaultValue, $key) use ($settings) {
$vuexyKey = 'vuexy_' . $key; // Convertir clave al formato de la base de datos
// Obtener valor desde la base de datos o usar el predeterminado
$value = $settings[$vuexyKey] ?? $defaultValue;
// Forzar booleanos para claves específicas
if (in_array($key, ['displayCustomizer', 'footerFixed', 'menuFixed', 'menuCollapsed', 'showDropdownOnHover'])) {
$value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
return [$key => $value];
})
->toArray();
}
/**
* Obtiene los paths de favicon en distintos tamaños.
*/
private function getFaviconPaths(array $settings): array
{
$defaultFavicon = config('_var.appFavicon');
$namespace = $settings['admin_favicon_ns'] ?? null;
return [
'namespace' => $namespace,
'16x16' => $namespace ? "{$namespace}_16x16.png" : $defaultFavicon,
'76x76' => $namespace ? "{$namespace}_76x76.png" : $defaultFavicon,
'120x120' => $namespace ? "{$namespace}_120x120.png" : $defaultFavicon,
'152x152' => $namespace ? "{$namespace}_152x152.png" : $defaultFavicon,
'180x180' => $namespace ? "{$namespace}_180x180.png" : $defaultFavicon,
'192x192' => $namespace ? "{$namespace}_192x192.png" : $defaultFavicon,
];
}
/**
* Obtiene los paths de los logos en distintos tamaños.
*/
private function getImageLogoPaths(array $settings): array
{
$defaultLogo = config('_var.appLogo');
return [
'small' => $this->getImagePath($settings, 'admin_image_logo_small', $defaultLogo),
'medium' => $this->getImagePath($settings, 'admin_image_logo_medium', $defaultLogo),
'large' => $this->getImagePath($settings, 'admin_image_logo', $defaultLogo),
'small_dark' => $this->getImagePath($settings, 'admin_image_logo_small_dark', $defaultLogo),
'medium_dark' => $this->getImagePath($settings, 'admin_image_logo_medium_dark', $defaultLogo),
'large_dark' => $this->getImagePath($settings, 'admin_image_logo_dark', $defaultLogo),
];
}
/**
* Obtiene un path de imagen o retorna un valor predeterminado.
*/
private function getImagePath(array $settings, string $key, string $default): string
{
return $settings[$key] ?? $default;
}
public static function clearAdminVarsCache()
{
Cache::forget("admin_settings");
}
}

View File

@ -0,0 +1,235 @@
<?php
namespace Modules\Admin\App\Services;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
class CacheConfigService
{
public function getConfig(): array
{
return [
'cache' => $this->getCacheConfig(),
'session' => $this->getSessionConfig(),
'database' => $this->getDatabaseConfig(),
'driver' => $this->getDriverVersion(),
'memcachedInUse' => $this->isDriverInUse('memcached'),
'redisInUse' => $this->isDriverInUse('redis'),
];
}
private function getCacheConfig(): array
{
$cacheConfig = Config::get('cache');
$driver = $cacheConfig['default'];
switch ($driver) {
case 'redis':
$connection = config('database.redis.cache');
$cacheConfig['host'] = $connection['host'] ?? 'localhost';
$cacheConfig['database'] = $connection['database'] ?? 'N/A';
break;
case 'database':
$connection = config('database.connections.' . config('cache.stores.database.connection'));
$cacheConfig['host'] = $connection['host'] ?? 'localhost';
$cacheConfig['database'] = $connection['database'] ?? 'N/A';
break;
case 'memcached':
$servers = config('cache.stores.memcached.servers');
$cacheConfig['host'] = $servers[0]['host'] ?? 'localhost';
$cacheConfig['database'] = 'N/A';
break;
case 'file':
$cacheConfig['host'] = storage_path('framework/cache/data');
$cacheConfig['database'] = 'N/A';
break;
default:
$cacheConfig['host'] = 'N/A';
$cacheConfig['database'] = 'N/A';
break;
}
return $cacheConfig;
}
private function getSessionConfig(): array
{
$sessionConfig = Config::get('session');
$driver = $sessionConfig['driver'];
switch ($driver) {
case 'redis':
$connection = config('database.redis.sessions');
$sessionConfig['host'] = $connection['host'] ?? 'localhost';
$sessionConfig['database'] = $connection['database'] ?? 'N/A';
break;
case 'database':
$connection = config('database.connections.' . $sessionConfig['connection']);
$sessionConfig['host'] = $connection['host'] ?? 'localhost';
$sessionConfig['database'] = $connection['database'] ?? 'N/A';
break;
case 'memcached':
$servers = config('cache.stores.memcached.servers');
$sessionConfig['host'] = $servers[0]['host'] ?? 'localhost';
$sessionConfig['database'] = 'N/A';
break;
case 'file':
$sessionConfig['host'] = storage_path('framework/sessions');
$sessionConfig['database'] = 'N/A';
break;
default:
$sessionConfig['host'] = 'N/A';
$sessionConfig['database'] = 'N/A';
break;
}
return $sessionConfig;
}
private function getDatabaseConfig(): array
{
$databaseConfig = Config::get('database');
$connection = $databaseConfig['default'];
$connectionConfig = config('database.connections.' . $connection);
$databaseConfig['host'] = $connectionConfig['host'] ?? 'localhost';
$databaseConfig['database'] = $connectionConfig['database'] ?? 'N/A';
return $databaseConfig;
}
private function getDriverVersion(): array
{
$drivers = [];
$defaultDatabaseDriver = config('database.default'); // Obtén el driver predeterminado
switch ($defaultDatabaseDriver) {
case 'mysql':
case 'mariadb':
$drivers['mysql'] = [
'version' => $this->getMySqlVersion(),
'details' => config("database.connections.$defaultDatabaseDriver"),
];
$drivers['mariadb'] = $drivers['mysql'];
case 'pgsql':
$drivers['pgsql'] = [
'version' => $this->getPgSqlVersion(),
'details' => config("database.connections.pgsql"),
];
break;
case 'sqlsrv':
$drivers['sqlsrv'] = [
'version' => $this->getSqlSrvVersion(),
'details' => config("database.connections.sqlsrv"),
];
break;
default:
$drivers['unknown'] = [
'version' => 'No disponible',
'details' => 'Driver no identificado',
];
break;
}
// Opcional: Agrega detalles de Redis y Memcached si están en uso
if ($this->isDriverInUse('redis')) {
$drivers['redis'] = [
'version' => $this->getRedisVersion(),
];
}
if ($this->isDriverInUse('memcached')) {
$drivers['memcached'] = [
'version' => $this->getMemcachedVersion(),
];
}
return $drivers;
}
private function getMySqlVersion(): string
{
try {
$version = DB::selectOne('SELECT VERSION() as version');
return $version->version ?? 'No disponible';
} catch (\Exception $e) {
return 'Error: ' . $e->getMessage();
}
}
private function getPgSqlVersion(): string
{
try {
$version = DB::selectOne("SHOW server_version");
return $version->server_version ?? 'No disponible';
} catch (\Exception $e) {
return 'Error: ' . $e->getMessage();
}
}
private function getSqlSrvVersion(): string
{
try {
$version = DB::selectOne("SELECT @@VERSION as version");
return $version->version ?? 'No disponible';
} catch (\Exception $e) {
return 'Error: ' . $e->getMessage();
}
}
private function getMemcachedVersion(): string
{
try {
$memcached = new \Memcached();
$memcached->addServer(
Config::get('cache.stores.memcached.servers.0.host'),
Config::get('cache.stores.memcached.servers.0.port')
);
$stats = $memcached->getStats();
foreach ($stats as $serverStats) {
return $serverStats['version'] ?? 'No disponible';
}
return 'No disponible';
} catch (\Exception $e) {
return 'Error: ' . $e->getMessage();
}
}
private function getRedisVersion(): string
{
try {
$info = Redis::info();
return $info['redis_version'] ?? 'No disponible';
} catch (\Exception $e) {
return 'Error: ' . $e->getMessage();
}
}
protected function isDriverInUse(string $driver): bool
{
return in_array($driver, [
Config::get('cache.default'),
Config::get('session.driver'),
Config::get('queue.default'),
]);
}
}

View File

@ -0,0 +1,389 @@
<?php
namespace Modules\Admin\App\Services;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\File;
class CacheManagerService
{
private string $driver;
public function __construct(string $driver = null)
{
$this->driver = $driver ?? config('cache.default');
}
/**
* Obtiene estadísticas de caché para el driver especificado.
*/
public function getCacheStats(string $driver = null): array
{
$driver = $driver ?? $this->driver;
if (!$this->isSupportedDriver($driver)) {
return $this->response('warning', 'Driver no soportado o no configurado.');
}
try {
return match ($driver) {
'database' => $this->_getDatabaseStats(),
'file' => $this->_getFilecacheStats(),
'redis' => $this->_getRedisStats(),
'memcached' => $this->_getMemcachedStats(),
default => $this->response('info', 'No hay estadísticas disponibles para este driver.'),
};
} catch (\Exception $e) {
return $this->response('danger', 'Error al obtener estadísticas: ' . $e->getMessage());
}
}
public function clearCache(string $driver = null): array
{
$driver = $driver ?? $this->driver;
if (!$this->isSupportedDriver($driver)) {
return $this->response('warning', 'Driver no soportado o no configurado.');
}
try {
switch ($driver) {
case 'redis':
$keysCleared = $this->clearRedisCache();
return $keysCleared
? $this->response('warning', 'Se ha purgado toda la caché de Redis.')
: $this->response('info', 'No se encontraron claves en Redis para eliminar.');
case 'memcached':
$keysCleared = $this->clearMemcachedCache();
return $keysCleared
? $this->response('warning', 'Se ha purgado toda la caché de Memcached.')
: $this->response('info', 'No se encontraron claves en Memcached para eliminar.');
case 'database':
$rowsDeleted = $this->clearDatabaseCache();
return $rowsDeleted
? $this->response('warning', 'Se ha purgado toda la caché almacenada en la base de datos.')
: $this->response('info', 'No se encontraron registros en la caché de la base de datos.');
case 'file':
$filesDeleted = $this->clearFilecache();
return $filesDeleted
? $this->response('warning', 'Se ha purgado toda la caché de archivos.')
: $this->response('info', 'No se encontraron archivos en la caché para eliminar.');
default:
Cache::flush();
return $this->response('warning', 'Caché purgada.');
}
} catch (\Exception $e) {
return $this->response('danger', 'Error al limpiar la caché: ' . $e->getMessage());
}
}
public function getRedisStats()
{
try {
if (!Redis::ping()) {
return $this->response('warning', 'No se puede conectar con el servidor Redis.');
}
$info = Redis::info();
$databases = $this->getRedisDatabases();
$redisInfo = [
'server' => config('database.redis.default.host'),
'redis_version' => $info['redis_version'] ?? 'N/A',
'os' => $info['os'] ?? 'N/A',
'tcp_port' => $info['tcp_port'] ?? 'N/A',
'connected_clients' => $info['connected_clients'] ?? 'N/A',
'blocked_clients' => $info['blocked_clients'] ?? 'N/A',
'maxmemory' => $info['maxmemory'] ?? 0,
'used_memory_human' => $info['used_memory_human'] ?? 'N/A',
'used_memory_peak' => $info['used_memory_peak'] ?? 'N/A',
'used_memory_peak_human' => $info['used_memory_peak_human'] ?? 'N/A',
'total_system_memory' => $info['total_system_memory'] ?? 0,
'total_system_memory_human' => $info['total_system_memory_human'] ?? 'N/A',
'maxmemory_human' => $info['maxmemory_human'] !== '0B' ? $info['maxmemory_human'] : 'Sin Límite',
'total_connections_received' => number_format($info['total_connections_received']) ?? 'N/A',
'total_commands_processed' => number_format($info['total_commands_processed']) ?? 'N/A',
'maxmemory_policy' => $info['maxmemory_policy'] ?? 'N/A',
'role' => $info['role'] ?? 'N/A',
'cache_database' => '',
'sessions_database' => '',
'general_database' => ',',
'keys' => $databases['total_keys'],
'used_memory' => $info['used_memory'] ?? 0,
'uptime' => gmdate('H\h i\m s\s', $info['uptime_in_seconds'] ?? 0),
'databases' => $databases,
];
return $this->response('success', 'Se a recargado las estadísticas de Redis.', ['info' => $redisInfo]);
} catch (\Exception $e) {
return $this->response('danger', 'Error al conectar con el servidor Redis: ' . Redis::getLastError());
}
}
public function getMemcachedStats()
{
try {
$memcachedStats = [];
// Crear instancia del cliente Memcached
$memcached = new \Memcached();
$memcached->addServer(config('memcached.host'), config('memcached.port'));
// Obtener estadísticas del servidor
$stats = $memcached->getStats();
foreach ($stats as $server => $data) {
$server = explode(':', $server);
$memcachedStats[] = [
'server' => $server[0],
'tcp_port' => $server[1],
'uptime' => $data['uptime'] ?? 'N/A',
'version' => $data['version'] ?? 'N/A',
'libevent' => $data['libevent'] ?? 'N/A',
'max_connections' => $data['max_connections'] ?? 0,
'total_connections' => $data['total_connections'] ?? 0,
'rejected_connections' => $data['rejected_connections'] ?? 0,
'curr_items' => $data['curr_items'] ?? 0, // Claves almacenadas
'bytes' => $data['bytes'] ?? 0, // Memoria usada
'limit_maxbytes' => $data['limit_maxbytes'] ?? 0, // Memoria máxima
'cmd_get' => $data['cmd_get'] ?? 0, // Comandos GET ejecutados
'cmd_set' => $data['cmd_set'] ?? 0, // Comandos SET ejecutados
'get_hits' => $data['get_hits'] ?? 0, // GET exitosos
'get_misses' => $data['get_misses'] ?? 0, // GET fallidos
'evictions' => $data['evictions'] ?? 0, // Claves expulsadas
'bytes_read' => $data['bytes_read'] ?? 0, // Bytes leídos
'bytes_written' => $data['bytes_written'] ?? 0, // Bytes escritos
'total_items' => $data['total_items'] ?? 0,
];
}
return $this->response('success', 'Se a recargado las estadísticas de Memcached.', ['info' => $memcachedStats]);
} catch (\Exception $e) {
return $this->response('danger', 'Error al conectar con el servidor Memcached: ' . $e->getMessage());
}
}
/**
* Obtiene estadísticas para caché en base de datos.
*/
private function _getDatabaseStats(): array
{
try {
$recordCount = DB::table('cache')->count();
$tableInfo = DB::select("SHOW TABLE STATUS WHERE Name = 'cache'");
$memory_usage = isset($tableInfo[0]) ? $this->formatBytes($tableInfo[0]->Data_length + $tableInfo[0]->Index_length) : 'N/A';
return $this->response('success', 'Se ha recargado la información de la caché de base de datos.', ['item_count' => $recordCount, 'memory_usage' => $memory_usage]);
} catch (\Exception $e) {
return $this->response('danger', 'Error al obtener estadísticas de la base de datos: ' . $e->getMessage());
}
}
/**
* Obtiene estadísticas para caché en archivos.
*/
private function _getFilecacheStats(): array
{
try {
$cachePath = config('cache.stores.file.path');
$files = glob($cachePath . '/*');
$memory_usage = $this->formatBytes(array_sum(array_map('filesize', $files)));
return $this->response('success', 'Se ha recargado la información de la caché de archivos.', ['item_count' => count($files), 'memory_usage' => $memory_usage]);
} catch (\Exception $e) {
return $this->response('danger', 'Error al obtener estadísticas de archivos: ' . $e->getMessage());
}
}
private function _getRedisStats()
{
try {
$prefix = config('cache.prefix'); // Asegúrate de agregar el sufijo correcto si es necesario
$info = Redis::info();
$keys = Redis::connection('cache')->keys($prefix . '*');
$memory_usage = $this->formatBytes($info['used_memory'] ?? 0);
return $this->response('success', 'Se ha recargado la información de la caché de Redis.', ['item_count' => count($keys), 'memory_usage' => $memory_usage]);
} catch (\Exception $e) {
return $this->response('danger', 'Error al obtener estadísticas de Redis: ' . $e->getMessage());
}
}
public function _getMemcachedStats(): array
{
try {
// Obtener estadísticas generales del servidor
$stats = Cache::getStore()->getMemcached()->getStats();
if (empty($stats)) {
return $this->response('error', 'No se pudieron obtener las estadísticas del servidor Memcached.', ['item_count' => 0, 'memory_usage' => 0]);
}
// Usar el primer servidor configurado (en la mayoría de los casos hay uno)
$serverStats = array_shift($stats);
return $this->response(
'success',
'Estadísticas del servidor Memcached obtenidas correctamente.',
[
'item_count' => $serverStats['curr_items'] ?? 0, // Número total de claves
'memory_usage' => $this->formatBytes($serverStats['bytes'] ?? 0), // Memoria usada
'max_memory' => $this->formatBytes($serverStats['limit_maxbytes'] ?? 0), // Memoria máxima asignada
]
);
} catch (\Exception $e) {
return $this->response('danger', 'Error al obtener estadísticas de Memcached: ' . $e->getMessage());
}
}
private function getRedisDatabases(): array
{
// Verificar si Redis está en uso
$isRedisUsed = collect([
config('cache.default'),
config('session.driver'),
config('queue.default'),
])->contains('redis');
if (!$isRedisUsed) {
return []; // Si Redis no está en uso, devolver un arreglo vacío
}
// Configuraciones de bases de datos de Redis según su uso
$databases = [
'default' => config('database.redis.default.database', 0), // REDIS_DB
'cache' => config('database.redis.cache.database', 0), // REDIS_CACHE_DB
'sessions' => config('database.redis.sessions.database', 0), // REDIS_SESSION_DB
];
$result = [];
$totalKeys = 0;
// Recorrer solo las bases configuradas y activas
foreach ($databases as $type => $db) {
Redis::select($db); // Seleccionar la base de datos
$keys = Redis::dbsize(); // Contar las claves en la base
if ($keys > 0) {
$result[$type] = [
'database' => $db,
'keys' => $keys,
];
$totalKeys += $keys;
}
}
if (!empty($result)) {
$result['total_keys'] = $totalKeys;
}
return $result;
}
private function clearDatabaseCache(): bool
{
$count = DB::table(config('cache.stores.database.table'))->count();
if ($count > 0) {
DB::table(config('cache.stores.database.table'))->truncate();
return true;
}
return false;
}
private function clearFilecache(): bool
{
$cachePath = config('cache.stores.file.path');
$files = glob($cachePath . '/*');
if (!empty($files)) {
File::deleteDirectory($cachePath);
return true;
}
return false;
}
private function clearRedisCache(): bool
{
$prefix = config('cache.prefix', '');
$keys = Redis::connection('cache')->keys($prefix . '*');
if (!empty($keys)) {
Redis::connection('cache')->flushdb();
// Simulate cache clearing delay
sleep(1);
return true;
}
return false;
}
private function clearMemcachedCache(): bool
{
// Obtener el cliente Memcached directamente
$memcached = Cache::store('memcached')->getStore()->getMemcached();
// Ejecutar flush para eliminar todo
if ($memcached->flush()) {
// Simulate cache clearing delay
sleep(1);
return true;
}
return false;
}
/**
* Verifica si un driver es soportado.
*/
private function isSupportedDriver(string $driver): bool
{
return in_array($driver, ['redis', 'memcached', 'database', 'file']);
}
/**
* Convierte bytes en un formato legible.
*/
private function formatBytes($bytes)
{
$sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf('%.2f', $bytes / pow(1024, $factor)) . ' ' . $sizes[$factor];
}
/**
* Genera una respuesta estandarizada.
*/
private function response(string $status, string $message, array $data = []): array
{
return array_merge(compact('status', 'message'), $data);
}
}

View File

@ -0,0 +1,273 @@
<?php
namespace Modules\Admin\App\Services;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Schema;
use Modules\Admin\App\Models\Setting;
class GlobalSettingsService
{
/**
* Tiempo de vida del caché en minutos (30 días).
*/
private $cacheTTL = 60 * 24 * 30;
/**
* Actualiza o crea una configuración.
*/
public function updateSetting(string $key, string $value): bool
{
$setting = Setting::updateOrCreate(
['key' => $key],
['value' => trim($value)]
);
return $setting->save();
}
/**
* Carga y sobrescribe las configuraciones del sistema.
*/
public function loadSystemConfig(): void
{
try {
if (!Schema::hasTable('migrations')) {
// Base de datos no inicializada: usar valores predeterminados
$config = $this->getDefaultSystemConfig();
} else {
// Cargar configuración desde la caché o base de datos
$config = Cache::remember('global_system_config', $this->cacheTTL, function () {
$settings = Setting::global()
->where('key', 'LIKE', 'config.%')
->pluck('value', 'key')
->toArray();
return [
'servicesFacebook' => $this->buildServiceConfig($settings, 'config.services.facebook.', 'services.facebook'),
'servicesGoogle' => $this->buildServiceConfig($settings, 'config.services.google.', 'services.google'),
'custom' => $this->buildVuexyCustomConfig($settings),
];
});
}
// Aplicar configuración al sistema
Config::set('services.facebook', $config['servicesFacebook']);
Config::set('services.google', $config['servicesGoogle']);
Config::set('custom', $config['custom']);
} catch (\Exception $e) {
// Manejo silencioso de errores para evitar interrupciones
Config::set('services.facebook', config('services.facebook', []));
Config::set('services.google', config('services.google', []));
Config::set('custom', config('custom', []));
}
}
/**
* Devuelve una configuración predeterminada si la base de datos no está inicializada.
*/
private function getDefaultSystemConfig(): array
{
return [
'servicesFacebook' => config('services.facebook', [
'client_id' => '',
'client_secret' => '',
'redirect' => '',
]),
'servicesGoogle' => config('services.google', [
'client_id' => '',
'client_secret' => '',
'redirect' => '',
]),
'custom' => config('custom', []),
];
}
/**
* Verifica si un bloque de configuraciones está presente.
*/
protected function hasBlockConfig(array $settings, string $blockPrefix): bool
{
return array_key_exists($blockPrefix, array_filter($settings, fn($key) => str_starts_with($key, $blockPrefix), ARRAY_FILTER_USE_KEY));
}
/**
* Construye la configuración de un servicio (Facebook, Google, etc.).
*/
protected function buildServiceConfig(array $settings, string $blockPrefix, string $defaultConfigKey): array
{
if (!$this->hasBlockConfig($settings, $blockPrefix)) {
return config($defaultConfigKey);
}
return [
'client_id' => $settings["{$blockPrefix}client_id"] ?? '',
'client_secret' => $settings["{$blockPrefix}client_secret"] ?? '',
'redirect' => $settings["{$blockPrefix}redirect"] ?? '',
];
}
/**
* Construye la configuración personalizada de Vuexy.
*/
protected function buildVuexyCustomConfig(array $settings): array
{
// Configuración predeterminada del sistema
$defaultCustomConfig = config('custom', []);
// Convertimos las claves planas a un array multidimensional
$settingsNested = Arr::undot($settings);
// Navegamos hasta la parte relevante del array desanidado
$customSettings = $settingsNested['config']['custom'] ?? [];
// Fusionamos la configuración predeterminada con los valores del sistema
$mergedConfig = array_replace_recursive($defaultCustomConfig, $customSettings);
// Normalizamos los valores booleanos
return $this->normalizeBooleanFields($mergedConfig);
}
/**
* Normaliza los campos booleanos.
*/
protected function normalizeBooleanFields(array $config): array
{
$booleanFields = [
'myRTLSupport',
'myRTLMode',
'hasCustomizer',
'displayCustomizer',
'footerFixed',
'menuFixed',
'menuCollapsed',
'showDropdownOnHover',
];
foreach ($booleanFields as $field) {
if (isset($config['custom'][$field])) {
$config['custom'][$field] = (bool) $config['custom'][$field];
}
}
return $config;
}
/**
* Limpia el caché de la configuración del sistema.
*/
public static function clearSystemConfigCache(): void
{
Cache::forget('global_system_config');
}
/**
* Elimina las claves config.custom.* y limpia global_system_config
*/
public static function clearVuexyCustomConfig(): void
{
Setting::where('key', 'LIKE', 'config.custom.%')->delete();
Cache::forget('global_system_config');
}
/**
* Obtiene y sobrescribe la configuración de correo electrónico.
*/
public function getMailSystemConfig(): array
{
return Cache::remember('mail_system_config', $this->cacheTTL, function () {
$settings = Setting::global()
->where('key', 'LIKE', 'mail.%')
->pluck('value', 'key')
->toArray();
$defaultMailersSmtpVars = config('mail.mailers.smtp');
return [
'mailers' => [
'smtp' => array_merge($defaultMailersSmtpVars, [
'url' => $settings['mail.mailers.smtp.url'] ?? $defaultMailersSmtpVars['url'],
'host' => $settings['mail.mailers.smtp.host'] ?? $defaultMailersSmtpVars['host'],
'port' => $settings['mail.mailers.smtp.port'] ?? $defaultMailersSmtpVars['port'],
'encryption' => $settings['mail.mailers.smtp.encryption'] ?? $defaultMailersSmtpVars['encryption'],
'username' => $settings['mail.mailers.smtp.username'] ?? $defaultMailersSmtpVars['username'],
'password' => isset($settings['mail.mailers.smtp.password']) && !empty($settings['mail.mailers.smtp.password'])
? Crypt::decryptString($settings['mail.mailers.smtp.password'])
: $defaultMailersSmtpVars['password'],
'timeout' => $settings['mail.mailers.smtp.timeout'] ?? $defaultMailersSmtpVars['timeout'],
]),
],
'from' => [
'address' => $settings['mail.from.address'] ?? config('mail.from.address'),
'name' => $settings['mail.from.name'] ?? config('mail.from.name'),
],
'reply_to' => [
'method' => $settings['mail.reply_to.method'] ?? config('mail.reply_to.method'),
'email' => $settings['mail.reply_to.email'] ?? config('mail.reply_to.email'),
'name' => $settings['mail.reply_to.name'] ?? config('mail.reply_to.name'),
],
];
});
}
/**
* Limpia el caché de la configuración de correo electrónico.
*/
public static function clearMailSystemConfigCache(): void
{
Cache::forget('mail_system_config');
}
/*
protected function buildFortifyFeatures(array $settings): array
{
return array_filter([
!empty($settings['config.fortify.features.registration']) && $settings['config.fortify.features.registration'] == 1
? \Laravel\Fortify\Features::registration()
: null,
!empty($settings['config.fortify.features.resetPasswords']) && $settings['config.fortify.features.resetPasswords'] == 1
? \Laravel\Fortify\Features::resetPasswords()
: null,
!empty($settings['config.fortify.features.emailVerification']) && $settings['config.fortify.features.emailVerification'] == 1
? \Laravel\Fortify\Features::emailVerification()
: null,
!empty($settings['config.fortify.features.updateProfileInformation']) && $settings['config.fortify.features.updateProfileInformation'] == 1
? \Laravel\Fortify\Features::updateProfileInformation()
: null,
!empty($settings['config.fortify.features.updatePasswords']) && $settings['config.fortify.features.updatePasswords'] == 1
? \Laravel\Fortify\Features::updatePasswords()
: null,
!empty($settings['config.fortify.features.twoFactorAuthentication.confirm']) && $settings['config.fortify.features.twoFactorAuthentication.confirm'] == 1
? \Laravel\Fortify\Features::twoFactorAuthentication([
'confirm' => true,
'confirmPassword' => !empty($settings['config.fortify.features.twoFactorAuthentication.confirmPassword'])
&& $settings['config.fortify.features.twoFactorAuthentication.confirmPassword'] == 1,
'window' => $settings['config.fortify.features.twoFactorAuthentication.window'] ?? 1,
])
: null,
]);
}
protected function loadUserSettings()
{
if (Auth::check()) {
$userId = Auth::id();
// Cargar configuraciones del usuario desde la caché
return Cache::remember("user_settings_{$userId}", $this->cacheTTL, function () use ($userId) {
return \App\Models\Setting::forUser($userId)->pluck('value', 'key')->toArray();
});
}
return [];
}
*/
}

View File

@ -0,0 +1,153 @@
<?php
namespace Modules\Admin\App\Services;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
class SessionManagerService
{
private string $driver;
public function __construct(string $driver = null)
{
$this->driver = $driver ?? config('session.driver');
}
public function getSessionStats(string $driver = null): array
{
$driver = $driver ?? $this->driver;
if (!$this->isSupportedDriver($driver))
return $this->response('warning', 'Driver no soportado o no configurado.', ['session_count' => 0]);
try {
switch ($driver) {
case 'redis':
return $this->getRedisStats();
case 'database':
return $this->getDatabaseStats();
case 'file':
return $this->getFileStats();
default:
return $this->response('warning', 'Driver no reconocido.', ['session_count' => 0]);
}
} catch (\Exception $e) {
return $this->response('danger', 'Error al obtener estadísticas: ' . $e->getMessage(), ['session_count' => 0]);
}
}
public function clearSessions(string $driver = null): array
{
$driver = $driver ?? $this->driver;
if (!$this->isSupportedDriver($driver)) {
return $this->response('warning', 'Driver no soportado o no configurado.');
}
try {
switch ($driver) {
case 'redis':
return $this->clearRedisSessions();
case 'memcached':
Cache::getStore()->flush();
return $this->response('success', 'Se eliminó la memoria caché de sesiones en Memcached.');
case 'database':
DB::table('sessions')->truncate();
return $this->response('success', 'Se eliminó la memoria caché de sesiones en la base de datos.');
case 'file':
return $this->clearFileSessions();
default:
return $this->response('warning', 'Driver no reconocido.');
}
} catch (\Exception $e) {
return $this->response('danger', 'Error al limpiar las sesiones: ' . $e->getMessage());
}
}
private function getRedisStats()
{
$prefix = config('cache.prefix'); // Asegúrate de agregar el sufijo correcto si es necesario
$keys = Redis::connection('sessions')->keys($prefix . '*');
return $this->response('success', 'Se ha recargado la información de la caché de Redis.', ['session_count' => count($keys)]);
}
private function getDatabaseStats(): array
{
$sessionCount = DB::table('sessions')->count();
return $this->response('success', 'Se ha recargado la información de la base de datos.', ['session_count' => $sessionCount]);
}
private function getFileStats(): array
{
$cachePath = config('session.files');
$files = glob($cachePath . '/*');
return $this->response('success', 'Se ha recargado la información de sesiones de archivos.', ['session_count' => count($files)]);
}
/**
* Limpia sesiones en Redis.
*/
private function clearRedisSessions(): array
{
$prefix = config('cache.prefix', '');
$keys = Redis::connection('sessions')->keys($prefix . '*');
if (!empty($keys)) {
Redis::connection('sessions')->flushdb();
// Simulate cache clearing delay
sleep(1);
return $this->response('success', 'Se eliminó la memoria caché de sesiones en Redis.');
}
return $this->response('info', 'No se encontraron claves para eliminar en Redis.');
}
/**
* Limpia sesiones en archivos.
*/
private function clearFileSessions(): array
{
$cachePath = config('session.files');
$files = glob($cachePath . '/*');
if (!empty($files)) {
foreach ($files as $file) {
unlink($file);
}
return $this->response('success', 'Se eliminó la memoria caché de sesiones en archivos.');
}
return $this->response('info', 'No se encontraron sesiones en archivos para eliminar.');
}
private function isSupportedDriver(string $driver): bool
{
return in_array($driver, ['redis', 'memcached', 'database', 'file']);
}
/**
* Genera una respuesta estandarizada.
*/
private function response(string $status, string $message, array $data = []): array
{
return array_merge(compact('status', 'message'), $data);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,211 @@
<?php
namespace Modules\Admin\App\Services;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\ImageManager;
use Modules\Admin\App\Models\Setting;
class WebsiteSettingsService
{
private $driver;
private $imageDisk = 'public';
private $favicon_basePath = 'favicon/';
private $image_logo_basePath = 'images/logo/';
private $faviconsSizes = [
'180x180' => [180, 180],
'192x192' => [192, 192],
'152x152' => [152, 152],
'120x120' => [120, 120],
'76x76' => [76, 76],
'16x16' => [16, 16],
];
private $imageLogoMaxPixels1 = 22500; // Primera versión (px^2)
private $imageLogoMaxPixels2 = 75625; // Segunda versión (px^2) en Base64
private $imageLogoMaxPixels3 = 230400; // Tercera versión (px^2)
public function __construct()
{
$this->driver = config('image.driver', 'gd');
}
public function updateSetting(string $key, string $value): bool
{
$setting = Setting::updateOrCreate(
['key' => $key],
['value' => trim($value)]
);
return $setting->save();
}
public function processAndSaveFavicon($image): void
{
Storage::makeDirectory($this->imageDisk . '/' . $this->favicon_basePath);
// Eliminar favicons antiguos
$this->deleteOldFavicons();
// Guardar imagen original
$imageManager = new ImageManager($this->driver);
$imageName = uniqid('website_favicon_');
$image = $imageManager->read($image->getRealPath());
foreach ($this->faviconsSizes as $size => [$width, $height]) {
$resizedPath = $this->favicon_basePath . $imageName . "_{$size}.png";
$image->cover($width, $height);
Storage::disk($this->imageDisk)->put($resizedPath, $image->toPng(indexed: true));
}
$this->updateSetting('website_favicon_ns', $this->favicon_basePath . $imageName);
}
protected function deleteOldFavicons(): void
{
// Obtener el favicon actual desde la base de datos
$currentFavicon = Setting::where('key', 'website_favicon')->value('value');
if ($currentFavicon) {
$filePaths = [
$this->imageDisk . '/' . $currentFavicon,
$this->imageDisk . '/' . $currentFavicon . '_16x16.png',
$this->imageDisk . '/' . $currentFavicon . '_192x192.png',
$this->imageDisk . '/' . $currentFavicon . '_76x76.png',
$this->imageDisk . '/' . $currentFavicon . '_120x120.png',
$this->imageDisk . '/' . $currentFavicon . '_152x152.png',
$this->imageDisk . '/' . $currentFavicon . '_180x180.png',
];
foreach ($filePaths as $filePath) {
if (Storage::exists($filePath)) {
Storage::delete($filePath);
}
}
}
}
public function processAndSaveImageLogo($image, string $type = ''): void
{
// Crear directorio si no existe
Storage::makeDirectory($this->imageDisk . '/' . $this->image_logo_basePath);
// Eliminar imágenes antiguas
$this->deleteOldImageWebapp($type);
// Leer imagen original
$imageManager = new ImageManager($this->driver);
$image = $imageManager->read($image->getRealPath());
// Generar tres versiones con diferentes áreas máximas
$this->generateAndSaveImage($image, $type, $this->imageLogoMaxPixels1, 'small'); // Versión 1
$this->generateAndSaveImage($image, $type, $this->imageLogoMaxPixels2, 'medium'); // Versión 2
$this->generateAndSaveImage($image, $type, $this->imageLogoMaxPixels3); // Versión 3
$this->generateAndSaveImageAsBase64($image, $type, $this->imageLogoMaxPixels3); // Versión 3
}
private function generateAndSaveImage($image, string $type, int $maxPixels, string $suffix = ''): void
{
$imageClone = clone $image;
// Escalar imagen conservando aspecto
$this->resizeImageToMaxPixels($imageClone, $maxPixels);
$imageName = 'website_image_logo' . ($suffix ? '_' . $suffix : '') . ($type == 'dark' ? '_dark' : '');
// Generar nombre y ruta
$imageNameUid = uniqid($imageName . '_', ".png");
$resizedPath = $this->image_logo_basePath . $imageNameUid;
// Guardar imagen en PNG
Storage::disk($this->imageDisk)->put($resizedPath, $imageClone->toPng(indexed: true));
// Actualizar configuración
$this->updateSetting($imageName, $resizedPath);
}
private function resizeImageToMaxPixels($image, int $maxPixels)
{
// Obtener dimensiones originales de la imagen
$originalWidth = $image->width(); // Método para obtener el ancho
$originalHeight = $image->height(); // Método para obtener el alto
// Calcular el aspecto
$aspectRatio = $originalWidth / $originalHeight;
// Calcular dimensiones redimensionadas conservando aspecto
if ($aspectRatio > 1) { // Ancho es dominante
$newWidth = sqrt($maxPixels * $aspectRatio);
$newHeight = $newWidth / $aspectRatio;
} else { // Alto es dominante
$newHeight = sqrt($maxPixels / $aspectRatio);
$newWidth = $newHeight * $aspectRatio;
}
// Redimensionar la imagen
$image->resize(
round($newWidth), // Redondear para evitar problemas con números decimales
round($newHeight),
function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
}
);
return $image;
}
private function generateAndSaveImageAsBase64($image, string $type, int $maxPixels): void
{
$imageClone = clone $image;
// Redimensionar imagen conservando el aspecto
$this->resizeImageToMaxPixels($imageClone, $maxPixels);
// Convertir a Base64
$base64Image = (string) $imageClone->toJpg(40)->toDataUri();
// Guardar como configuración
$this->updateSetting(
"website_image_logo_base64" . ($type === 'dark' ? '_dark' : ''),
$base64Image // Ya incluye "data:image/png;base64,"
);
}
protected function deleteOldImageWebapp(string $type = ''): void
{
// Determinar prefijo según el tipo (normal o dark)
$suffix = $type === 'dark' ? '_dark' : '';
// Claves relacionadas con las imágenes que queremos limpiar
$imageKeys = [
"website_image_logo{$suffix}",
"website_image_logo_small{$suffix}",
"website_image_logo_medium{$suffix}",
"website_image_logo_base64{$suffix}",
];
foreach ($imageKeys as $key) {
// Obtener la ruta de la imagen actual desde la base de datos
$currentImage = Setting::where('key', $key)->value('value');
// Si es una imagen en disco, eliminarla
if ($currentImage && !str_starts_with($currentImage, 'data:image')) {
$filePath = $this->imageDisk . '/' . $currentImage;
if (Storage::exists($filePath)) {
Storage::delete($filePath);
}
}
// Opcional: Eliminar la configuración de la base de datos
Setting::where('key', $key)->delete();
}
}
}

View File

@ -0,0 +1,112 @@
<?php
namespace Koneko\VuexyAdminModule;
use Illuminate\Auth\Events\Login;
use Illuminate\Auth\Events\Logout;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Livewire\Livewire;
use Spatie\Permission\PermissionServiceProvider;
use Modules\Admin\App\Http\Middleware\AdminTemplateMiddleware;
use Modules\Admin\App\Listeners\ClearUserCache;
use Modules\Admin\App\Listeners\HandleUserLogin;
use Modules\Admin\App\Livewire\Cache\CacheFunctions;
use Modules\Admin\App\Livewire\Users\UserCount;
use Modules\Admin\App\Livewire\Users\UserTable;
use Modules\Admin\App\Livewire\Rbac\RoleCards;
use Modules\Admin\App\Livewire\Rbac\PermissionsIndex;
use Modules\Admin\App\Livewire\Cache\CacheStats;
use Modules\Admin\App\Livewire\Cache\SessionStats;
use Modules\Admin\App\Livewire\Cache\MemcachedStats;
use Modules\Admin\App\Livewire\Cache\RedisStats;
use Modules\Admin\App\Livewire\AdminSettings\ApplicationSettings;
use Modules\Admin\App\Livewire\AdminSettings\GeneralSettings;
use Modules\Admin\App\Livewire\AdminSettings\InterfaceSettings;
use Modules\Admin\App\Livewire\AdminSettings\MailSmtpSettings;
use Modules\Admin\App\Livewire\AdminSettings\MailSenderResponseSettings;
use Modules\Admin\App\Livewire\WebsiteSettings\AnalyticsSettings;
use Modules\Admin\App\Livewire\WebsiteSettings\ChatSettings;
use Modules\Admin\App\Livewire\WebsiteSettings\ContactFormSettings;
use Modules\Admin\App\Livewire\WebsiteSettings\ContactInfoSettings;
use Modules\Admin\App\Livewire\WebsiteSettings\FaviconSettings;
use Modules\Admin\App\Livewire\WebsiteSettings\ImageLogoSettings;
use Modules\Admin\App\Livewire\WebsiteSettings\LocationSettings;
use Modules\Admin\App\Livewire\WebsiteSettings\SocialSettings;
use Modules\Admin\App\Livewire\WebsiteSettings\TemplateSettings;
use Modules\Admin\App\Livewire\WebsiteSettings\WebsiteSettings;
use Modules\Admin\App\Livewire\WebsiteSettings\LegalSettings;
use Modules\Admin\App\Models\User;
use Modules\Admin\App\Providers\FortifyServiceProvider;
use Modules\Admin\App\Providers\GlobalSettingsProvider;
use Modules\Admin\App\Providers\VuexyAdminProvider;
use OwenIt\Auditing\AuditableObserver;
class BaseServiceProvider extends ServiceProvider
{
public static $prefixRoute = 'admin';
public function register()
{
// Register the module's services and providers
$this->app->register(FortifyServiceProvider::class);
$this->app->register(PermissionServiceProvider::class);
$this->app->register(GlobalSettingsProvider::class);
$this->app->register(VuexyAdminProvider::class);
}
public function boot()
{
// Registrar alias del middleware
$this->app['router']->aliasMiddleware('admin.settings', AdminTemplateMiddleware::class);
// Register the module's routes
Route::middleware(['web'])->group(__DIR__ . '/routes.php');
// Register the module's views and resources
$this->loadViewsFrom(__DIR__ . '/Resources/views', 'admin');
// Register the migrations
$this->loadMigrationsFrom(__DIR__ . '/Database/migrations');
// Registrar manualmente listeners
Event::listen(Login::class, HandleUserLogin::class);
Event::listen(Logout::class, ClearUserCache::class);
// Register the module's Livewire components
Livewire::component('user-count', UserCount::class);
Livewire::component('user-table', UserTable::class);
Livewire::component('role-card', RoleCards::class);
Livewire::component('permissions-index', PermissionsIndex::class);
Livewire::component('general-settings', GeneralSettings::class);
Livewire::component('application-settings', ApplicationSettings::class);
Livewire::component('interface-settings', InterfaceSettings::class);
Livewire::component('cache-stats', CacheStats::class);
Livewire::component('session-stats', SessionStats::class);
Livewire::component('redis-stats', RedisStats::class);
Livewire::component('memcached-stats', MemcachedStats::class);
Livewire::component('cache-functions', CacheFunctions::class);
Livewire::component('mail-smtp-settings', MailSmtpSettings::class);
Livewire::component('mail-sender-response-settings', MailSenderResponseSettings::class);
Livewire::component('website-settings', WebsiteSettings::class);
Livewire::component('website-favicon-settings', FaviconSettings::class);
Livewire::component('website-image-logo-settings', ImageLogoSettings::class);
Livewire::component('website-social-settings', SocialSettings::class);
Livewire::component('website-contact-form-settings', ContactFormSettings::class);
Livewire::component('website-contact-info-settings', ContactInfoSettings::class);
Livewire::component('website-location-settings', LocationSettings::class);
Livewire::component('website-chat-settings', ChatSettings::class);
Livewire::component('website-analytics-settings', AnalyticsSettings::class);
Livewire::component('website-template-settings', TemplateSettings::class);
Livewire::component('website-legal-settings', LegalSettings::class);
// Enable auditing
User::observe(AuditableObserver::class);
}
}

Some files were not shown because too many files have changed in this diff Show More