Prepare modules
This commit is contained in:
parent
04f5e973d7
commit
3916c62935
37
Console/Commands/SitemapGenerate.php
Normal file
37
Console/Commands/SitemapGenerate.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Koneko\VuexyWebsiteAdmin\Models\SitemapUrl;
|
||||
|
||||
class SitemapGenerate extends Command
|
||||
{
|
||||
protected $signature = 'sitemap:generate';
|
||||
protected $description = 'Genera un sitemap.xml con rutas dinámicas del sistema';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$urls = SitemapUrl::where('is_active', true)->get();
|
||||
|
||||
$sitemap = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL;
|
||||
$sitemap .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . PHP_EOL;
|
||||
|
||||
foreach ($urls as $url) {
|
||||
$sitemap .= " <url><loc>{$url->url}</loc>" . PHP_EOL;
|
||||
$sitemap .= " <changefreq>{$url->changefreq}</changefreq>" . PHP_EOL;
|
||||
$sitemap .= " <priority>{$url->priority}</priority>" . PHP_EOL;
|
||||
if ($url->lastmod) {
|
||||
$sitemap .= " <lastmod>{$url->lastmod->toDateString()}</lastmod>" . PHP_EOL;
|
||||
}
|
||||
$sitemap .= " </url>" . PHP_EOL;
|
||||
}
|
||||
|
||||
$sitemap .= '</urlset>';
|
||||
|
||||
Storage::disk('public')->put('sitemap.xml', $sitemap);
|
||||
|
||||
$this->info('✅ Sitemap generado en storage/app/public/sitemap.xml');
|
||||
}
|
||||
}
|
19
Http/Controllers/ChatController.php
Normal file
19
Http/Controllers/ChatController.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class ChatController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('vuexy-website-admin::chat.index');
|
||||
}
|
||||
|
||||
}
|
19
Http/Controllers/ContactFormController.php
Normal file
19
Http/Controllers/ContactFormController.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class ContactFormController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('vuexy-website-admin::contact-form.index');
|
||||
}
|
||||
|
||||
}
|
19
Http/Controllers/ContactInfoController.php
Normal file
19
Http/Controllers/ContactInfoController.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class ContactInfoController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('vuexy-website-admin::contact-info.index');
|
||||
}
|
||||
|
||||
}
|
19
Http/Controllers/FaqController.php
Normal file
19
Http/Controllers/FaqController.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class FaqController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('vuexy-website-admin::faq.index');
|
||||
}
|
||||
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Http\Controllers;;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Yajra\DataTables\Facades\DataTables;
|
||||
use App\Models\Catalog\DropdownList;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class GeneralController extends Controller
|
||||
{
|
||||
public function webApp()
|
||||
{
|
||||
$breadcrumbs = [
|
||||
['route' => 'admin.home', 'name' => "Inicio"],
|
||||
['name' => "Ajustes"],
|
||||
['name' => "General"],
|
||||
['name' => "Aplicación web", 'active' => true],
|
||||
];
|
||||
|
||||
return view('admin.settings.general.webapp-index', compact('breadcrumbs'));
|
||||
}
|
||||
|
||||
public function store()
|
||||
{
|
||||
$breadcrumbs = [
|
||||
['route' => 'admin.home', 'name' => "Inicio"],
|
||||
['name' => "Ajustes"],
|
||||
['name' => "General"],
|
||||
['name' => "Empresa", 'active' => true],
|
||||
];
|
||||
|
||||
return view('admin.settings.general.store-index', compact('breadcrumbs'));
|
||||
}
|
||||
|
||||
public function divisas(Request $request)
|
||||
{
|
||||
if ($request->ajax()) {
|
||||
$query = DropdownList::select(
|
||||
'dropdown_lists.id',
|
||||
'dropdown_lists.single',
|
||||
'sat_moneda.descripcion',
|
||||
'dropdown_lists.param1',
|
||||
'dropdown_lists.param2',
|
||||
'dropdown_lists.param3',
|
||||
'dropdown_lists.param4',
|
||||
'dropdown_lists.created_at'
|
||||
)
|
||||
->Join('sat_moneda', 'dropdown_lists.single', '=', 'sat_moneda.c_moneda')
|
||||
->where('dropdown_lists.label', DropdownList::SYS_DIVISA);
|
||||
|
||||
// Manejar el ordenamiento del lado del servidor basado en las columnas que DataTables solicita
|
||||
if ($request->has('order')) {
|
||||
$columns = [2 => 'single', 'descripcion', 'param1', 'param2', 'param3', 'param4', 'created_at'];
|
||||
|
||||
foreach ($request->get('order') as $order) {
|
||||
$query->orderBy($columns[$order['column']], $order['dir']);
|
||||
}
|
||||
}
|
||||
|
||||
$warehouses = $query->get();
|
||||
|
||||
return DataTables::of($warehouses)
|
||||
->only(['id', 'single', 'descripcion', 'param1', 'param2', 'param3', 'param4', 'created_at'])
|
||||
->addIndexColumn()
|
||||
->editColumn('created_at', function ($user) {
|
||||
return $user->created_at->format('Y-m-d');
|
||||
})
|
||||
->make(true);
|
||||
}
|
||||
|
||||
$breadcrumbs = [
|
||||
['route' => 'admin.home', 'name' => "Inicio"],
|
||||
['name' => "Ajustes"],
|
||||
['name' => "General"],
|
||||
['name' => "Divisas", 'active' => true],
|
||||
];
|
||||
|
||||
return view('admin.settings.general.divisas-index', compact('breadcrumbs'));
|
||||
}
|
||||
|
||||
public function warehouses(Request $request)
|
||||
{
|
||||
if ($request->ajax()) {
|
||||
$query = DropdownList::select(
|
||||
'dropdown_lists.id',
|
||||
'dropdown_lists.single',
|
||||
'dropdown_lists.param1',
|
||||
'dropdown_lists.status',
|
||||
'dropdown_lists.created_at'
|
||||
)
|
||||
->where('dropdown_lists.label', DropdownList::SYS_WAREHOUSE);
|
||||
|
||||
// Manejar el ordenamiento del lado del servidor basado en las columnas que DataTables solicita
|
||||
if ($request->has('order')) {
|
||||
$columns = [2 => 'single', 'param1', 'status', 'created_at'];
|
||||
|
||||
foreach ($request->get('order') as $order) {
|
||||
$query->orderBy($columns[$order['column']], $order['dir']);
|
||||
}
|
||||
}
|
||||
|
||||
$warehouses = $query->get();
|
||||
|
||||
return DataTables::of($warehouses)
|
||||
->only(['id', 'single', 'param1', 'status', 'created_at'])
|
||||
->addIndexColumn()
|
||||
->editColumn('created_at', function ($user) {
|
||||
return $user->created_at->format('Y-m-d');
|
||||
})
|
||||
->make(true);
|
||||
}
|
||||
|
||||
$breadcrumbs = [
|
||||
['route' => 'admin.home', 'name' => "Inicio"],
|
||||
['name' => "Ajustes"],
|
||||
['name' => "General"],
|
||||
['name' => "Almacenes", 'active' => true],
|
||||
];
|
||||
|
||||
return view('admin.settings.general.warehouses-index', compact('breadcrumbs'));
|
||||
}
|
||||
|
||||
public function formasPago2()
|
||||
{
|
||||
$breadcrumbs = [
|
||||
['route' => 'admin.home', 'name' => "Inicio"],
|
||||
['name' => "Ajustes"],
|
||||
['name' => "General"],
|
||||
['name' => "Formas de pago ²", 'active' => true],
|
||||
];
|
||||
|
||||
return view('admin.settings.general.formas-pago-2-index', compact('breadcrumbs'));
|
||||
}
|
||||
|
||||
public function apiBanxico()
|
||||
{
|
||||
$breadcrumbs = [
|
||||
['route' => 'admin.home', 'name' => "Inicio"],
|
||||
['name' => "Ajustes"],
|
||||
['name' => "General"],
|
||||
['name' => "API BANXICO", 'active' => true],
|
||||
];
|
||||
|
||||
return view('admin.settings.general.api-banxico-index', compact('breadcrumbs'));
|
||||
}
|
||||
|
||||
public function smtp()
|
||||
{
|
||||
$breadcrumbs = [
|
||||
['route' => 'admin.home', 'name' => "Inicio"],
|
||||
['name' => "Ajustes"],
|
||||
['name' => "General"],
|
||||
['name' => "Servidor de correo SMTP", 'active' => true],
|
||||
];
|
||||
|
||||
return view('admin.settings.general.smtp-index', compact('breadcrumbs'));
|
||||
}
|
||||
|
||||
public function checkUniqueWarehouse(Request $request)
|
||||
{
|
||||
$id = $request->input('id');
|
||||
$single = $request->input('single');
|
||||
|
||||
$exists = DropdownList::where('single', $single)
|
||||
->where('label', DropdownList::SYS_WAREHOUSE)
|
||||
->where('id', '!=', $id) // Excluir el registro actual
|
||||
->exists();
|
||||
|
||||
return response()->json(['isUnique' => !$exists]);
|
||||
}
|
||||
|
||||
public function checkUniqueDivisa(Request $request)
|
||||
{
|
||||
$id = $request->input('id');
|
||||
$single = $request->input('single');
|
||||
|
||||
$exists = DropdownList::where('single', $single)
|
||||
->where('label', DropdownList::SYS_DIVISA)
|
||||
->where('id', '!=', $id) // Excluir el registro actual
|
||||
->exists();
|
||||
|
||||
return response()->json(['isUnique' => !$exists]);
|
||||
}
|
||||
|
||||
}
|
19
Http/Controllers/GoogleAnalyticsController.php
Normal file
19
Http/Controllers/GoogleAnalyticsController.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class GoogleAnalyticsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('vuexy-website-admin::google-analytics.index');
|
||||
}
|
||||
|
||||
}
|
19
Http/Controllers/ImagesController.php
Normal file
19
Http/Controllers/ImagesController.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class ImagesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('vuexy-website-admin::images.index');
|
||||
}
|
||||
|
||||
}
|
19
Http/Controllers/LegalNoticesController.php
Normal file
19
Http/Controllers/LegalNoticesController.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class LegalNoticesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('vuexy-website-admin::legal-notices.index');
|
||||
}
|
||||
|
||||
}
|
19
Http/Controllers/SitemapController.php
Normal file
19
Http/Controllers/SitemapController.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class SitemapController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('vuexy-website-admin::sitemap-manager.index');
|
||||
}
|
||||
|
||||
}
|
19
Http/Controllers/SocialMediaController.php
Normal file
19
Http/Controllers/SocialMediaController.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class SocialMediaController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('vuexy-website-admin::social-media.index');
|
||||
}
|
||||
|
||||
}
|
19
Http/Controllers/VuexyWebsiteAdminController.php
Normal file
19
Http/Controllers/VuexyWebsiteAdminController.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class VuexyWebsiteAdminController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('vuexy-website-admin::general-settings.index');
|
||||
}
|
||||
|
||||
}
|
103
Livewire/Faq/FaqIndex.php
Normal file
103
Livewire/Faq/FaqIndex.php
Normal file
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\Faq;
|
||||
|
||||
use Koneko\VuexyAdmin\Livewire\Table\AbstractIndexComponent;
|
||||
use Koneko\VuexyWebsiteAdmin\Models\Faq;
|
||||
|
||||
class FaqIndex extends AbstractIndexComponent
|
||||
{
|
||||
/**
|
||||
* Retorna la clase del modelo asociado.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function model(): string
|
||||
{
|
||||
return Faq::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configura el encabezado (header) de la tabla (las columnas).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function columns(): array
|
||||
{
|
||||
return [
|
||||
'action' => 'Acciones',
|
||||
'status' => 'Estatus',
|
||||
'created_at' => 'Fecha de Creación',
|
||||
'updated_at' => 'Última Actualización',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Define los formatos de cada columna (se inyectará en $bt_datatable['format']).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function format(): array
|
||||
{
|
||||
return [
|
||||
'action' => [
|
||||
'formatter' => 'FaqActionFormatter',
|
||||
'onlyFormatter' => true,
|
||||
],
|
||||
|
||||
'status' => [
|
||||
'formatter' => [
|
||||
'name' => 'dynamicBooleanFormatter',
|
||||
'params' => ['tag' => 'activo']
|
||||
],
|
||||
'align' => 'center',
|
||||
],
|
||||
'created_at' => [
|
||||
'formatter' => 'textNowrapFormatter',
|
||||
'align' => 'center',
|
||||
'visible' => false,
|
||||
],
|
||||
'updated_at' => [
|
||||
'formatter' => 'textNowrapFormatter',
|
||||
'align' => 'center',
|
||||
'visible' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna la configuración base (común) para la tabla Bootstrap Table.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function bootstraptableConfig(): array
|
||||
{
|
||||
return [
|
||||
'sortName' => 'code',
|
||||
'exportFileName' => 'Almacenes',
|
||||
'showFullscreen' => false,
|
||||
'showPaginationSwitch' => false,
|
||||
'showRefresh' => false,
|
||||
'pagination' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna la ruta de la vista Blade.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function viewPath(): string
|
||||
{
|
||||
// La vista que ya tienes creada para FaqIndex
|
||||
return 'vuexy-website-admin::livewire.faq.index';
|
||||
}
|
||||
|
||||
/**
|
||||
* Métodos que necesites sobreescribir o extender.
|
||||
*/
|
||||
public function mount(): void
|
||||
{
|
||||
parent::mount();
|
||||
}
|
||||
}
|
217
Livewire/Faq/FaqOffcanvasForm.php
Normal file
217
Livewire/Faq/FaqOffcanvasForm.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\Faq;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Koneko\VuexyAdmin\Livewire\Form\AbstractFormOffCanvasComponent;
|
||||
use Koneko\VuexyContacts\Services\ContactCatalogService;
|
||||
use Koneko\VuexyStoreManager\Services\StoreCatalogService;
|
||||
use Koneko\VuexyWarehouse\Models\Warehouse;
|
||||
|
||||
/**
|
||||
* Class FaqOffcanvasForm
|
||||
*
|
||||
* Componente Livewire para gestionar almacenes.
|
||||
* Extiende la clase AbstractFormOffCanvasComponent e implementa validaciones dinámicas,
|
||||
* manejo de formularios, eventos y actualizaciones en tiempo real.
|
||||
*
|
||||
* @package Koneko\VuexyWarehouse\Livewire\Warehouses
|
||||
*/
|
||||
class FaqOffcanvasForm extends AbstractFormOffCanvasComponent
|
||||
{
|
||||
/**
|
||||
* Propiedades del formulario relacionadas con el almacén.
|
||||
*/
|
||||
public $id, $store_id, $work_center_id, $code, $name, $description,
|
||||
$manager_id, $tel, $tel2, $priority, $status, $confirmDeletion;
|
||||
|
||||
/**
|
||||
* Listas de opciones para selects en el formulario.
|
||||
*/
|
||||
public $store_options = [],
|
||||
$work_center_options = [],
|
||||
$manager_options = [];
|
||||
|
||||
/**
|
||||
* Eventos de escucha de Livewire.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $listeners = [
|
||||
'editWarehouse' => 'loadFormModel',
|
||||
'confirmDeletionWarehouse' => 'loadFormModelForDeletion',
|
||||
];
|
||||
|
||||
/**
|
||||
* Definición de tipos de datos que se deben castear.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'status' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Define el modelo Eloquent asociado con el formulario.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function model(): string
|
||||
{
|
||||
return Warehouse::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define los campos del formulario.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function fields(): array
|
||||
{
|
||||
return (new Warehouse())->getFillable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Valores por defecto para el formulario.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function defaults(): array
|
||||
{
|
||||
return [
|
||||
'priority' => 0,
|
||||
'status' => true,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Campo que se debe enfocar cuando se abra el formulario.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function focusOnOpen(): string
|
||||
{
|
||||
return 'code';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define reglas de validación dinámicas basadas en el modo actual.
|
||||
*
|
||||
* @param string $mode El modo actual del formulario ('create', 'edit', 'delete').
|
||||
* @return array
|
||||
*/
|
||||
protected function dynamicRules(string $mode): array
|
||||
{
|
||||
switch ($mode) {
|
||||
case 'create':
|
||||
case 'edit':
|
||||
return [
|
||||
'store_id' => ['required', 'integer', 'exists:stores,id'],
|
||||
'work_center_id' => ['nullable', 'integer', 'exists:store_work_centers,id'],
|
||||
'code' => ['required', 'string', 'max:16', Rule::unique('warehouses', 'code')->ignore($this->id)],
|
||||
'name' => ['required', 'string', 'max:96'],
|
||||
'description' => ['nullable', 'string', 'max:1024'],
|
||||
'manager_id' => ['nullable', 'integer', 'exists:users,id'],
|
||||
'tel' => ['nullable', 'regex:/^[0-9+\-\s]+$/', 'max:20'],
|
||||
'tel2' => ['nullable', 'regex:/^[0-9+\-\s]+$/', 'max:20'],
|
||||
'priority' => ['nullable', 'numeric', 'between:0,99'],
|
||||
'status' => ['nullable', 'boolean'],
|
||||
];
|
||||
|
||||
case 'delete':
|
||||
return [
|
||||
'confirmDeletion' => 'accepted', // Asegura que el usuario confirme la eliminación
|
||||
];
|
||||
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== VALIDACIONES =====================
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function attributes(): array
|
||||
{
|
||||
return [
|
||||
'code' => 'código de almacén',
|
||||
'name' => 'nombre del almacén',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error messages for the defined validation rules.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function messages(): array
|
||||
{
|
||||
return [
|
||||
'store_id.required' => 'El almacén debe estar asociado a un negocio.',
|
||||
'code.required' => 'El código del almacén es obligatorio.',
|
||||
'code.unique' => 'Este código ya está en uso por otro almacén.',
|
||||
'name.required' => 'El nombre del almacén es obligatorio.',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Carga el formulario con datos del almacén y actualiza las opciones dinámicas.
|
||||
*
|
||||
* @param int $id
|
||||
*/
|
||||
public function loadFormModel($id): void
|
||||
{
|
||||
parent::loadFormModel($id);
|
||||
|
||||
$this->work_center_options = DB::table('store_work_centers')
|
||||
->where('store_id', $this->store_id)
|
||||
->pluck('name', 'id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Carga el formulario para eliminar un almacén, actualizando las opciones necesarias.
|
||||
*
|
||||
* @param int $id
|
||||
*/
|
||||
public function loadFormModelForDeletion($id): void
|
||||
{
|
||||
parent::loadFormModelForDeletion($id);
|
||||
|
||||
$this->work_center_options = DB::table('store_work_centers')
|
||||
->where('store_id', $this->store_id)
|
||||
->pluck('name', 'id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define las opciones de los selectores desplegables.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function options(): array
|
||||
{
|
||||
$storeCatalogService = app(StoreCatalogService::class);
|
||||
$contactCatalogService = app(ContactCatalogService::class);
|
||||
|
||||
return [
|
||||
'store_options' => $storeCatalogService->searchCatalog('stores', '', ['limit' => -1]),
|
||||
'manager_options' => $contactCatalogService->searchCatalog('users', '', ['limit' => -1]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ruta de la vista asociada con este formulario.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function viewPath(): string
|
||||
{
|
||||
return 'vuexy-website-admin::livewire.faq.offcanvas-form';
|
||||
}
|
||||
}
|
104
Livewire/Images/ImagesIndex.php
Normal file
104
Livewire/Images/ImagesIndex.php
Normal file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\Images;
|
||||
|
||||
use Koneko\VuexyAdmin\Livewire\Table\AbstractIndexComponent;
|
||||
use Koneko\VuexyWebsiteAdmin\Models\Faq;
|
||||
use Koneko\VuexyWebsiteAdmin\Models\Images;
|
||||
|
||||
class ImagesIndex extends AbstractIndexComponent
|
||||
{
|
||||
/**
|
||||
* Retorna la clase del modelo asociado.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function model(): string
|
||||
{
|
||||
return Faq::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configura el encabezado (header) de la tabla (las columnas).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function columns(): array
|
||||
{
|
||||
return [
|
||||
'action' => 'Acciones',
|
||||
'status' => 'Estatus',
|
||||
'created_at' => 'Fecha de Creación',
|
||||
'updated_at' => 'Última Actualización',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Define los formatos de cada columna (se inyectará en $bt_datatable['format']).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function format(): array
|
||||
{
|
||||
return [
|
||||
'action' => [
|
||||
'formatter' => 'ImagesActionFormatter',
|
||||
'onlyFormatter' => true,
|
||||
],
|
||||
|
||||
'status' => [
|
||||
'formatter' => [
|
||||
'name' => 'dynamicBooleanFormatter',
|
||||
'params' => ['tag' => 'activo']
|
||||
],
|
||||
'align' => 'center',
|
||||
],
|
||||
'created_at' => [
|
||||
'formatter' => 'textNowrapFormatter',
|
||||
'align' => 'center',
|
||||
'visible' => false,
|
||||
],
|
||||
'updated_at' => [
|
||||
'formatter' => 'textNowrapFormatter',
|
||||
'align' => 'center',
|
||||
'visible' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna la configuración base (común) para la tabla Bootstrap Table.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function bootstraptableConfig(): array
|
||||
{
|
||||
return [
|
||||
'sortName' => 'code',
|
||||
'exportFileName' => 'Almacenes',
|
||||
'showFullscreen' => false,
|
||||
'showPaginationSwitch' => false,
|
||||
'showRefresh' => false,
|
||||
'pagination' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna la ruta de la vista Blade.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function viewPath(): string
|
||||
{
|
||||
// La vista que ya tienes creada para ImagesIndex
|
||||
return 'vuexy-website-admin::livewire.images.index';
|
||||
}
|
||||
|
||||
/**
|
||||
* Métodos que necesites sobreescribir o extender.
|
||||
*/
|
||||
public function mount(): void
|
||||
{
|
||||
parent::mount();
|
||||
}
|
||||
}
|
217
Livewire/LegalNotices/LegalNoticeOffCanvasForm.php
Normal file
217
Livewire/LegalNotices/LegalNoticeOffCanvasForm.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\LegalNotices;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Koneko\VuexyAdmin\Livewire\Form\AbstractFormOffCanvasComponent;
|
||||
use Koneko\VuexyContacts\Services\ContactCatalogService;
|
||||
use Koneko\VuexyStoreManager\Services\StoreCatalogService;
|
||||
use Koneko\VuexyWarehouse\Models\Warehouse;
|
||||
|
||||
/**
|
||||
* Class LegalNoticeOffCanvasForm
|
||||
*
|
||||
* Componente Livewire para gestionar almacenes.
|
||||
* Extiende la clase AbstractFormOffCanvasComponent e implementa validaciones dinámicas,
|
||||
* manejo de formularios, eventos y actualizaciones en tiempo real.
|
||||
*
|
||||
* @package Koneko\VuexyWarehouse\Livewire\Warehouses
|
||||
*/
|
||||
class LegalNoticeOffCanvasForm extends AbstractFormOffCanvasComponent
|
||||
{
|
||||
/**
|
||||
* Propiedades del formulario relacionadas con el almacén.
|
||||
*/
|
||||
public $id, $store_id, $work_center_id, $code, $name, $description,
|
||||
$manager_id, $tel, $tel2, $priority, $status, $confirmDeletion;
|
||||
|
||||
/**
|
||||
* Listas de opciones para selects en el formulario.
|
||||
*/
|
||||
public $store_options = [],
|
||||
$work_center_options = [],
|
||||
$manager_options = [];
|
||||
|
||||
/**
|
||||
* Eventos de escucha de Livewire.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $listeners = [
|
||||
'editWarehouse' => 'loadFormModel',
|
||||
'confirmDeletionWarehouse' => 'loadFormModelForDeletion',
|
||||
];
|
||||
|
||||
/**
|
||||
* Definición de tipos de datos que se deben castear.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'status' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Define el modelo Eloquent asociado con el formulario.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function model(): string
|
||||
{
|
||||
return Warehouse::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define los campos del formulario.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function fields(): array
|
||||
{
|
||||
return (new Warehouse())->getFillable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Valores por defecto para el formulario.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function defaults(): array
|
||||
{
|
||||
return [
|
||||
'priority' => 0,
|
||||
'status' => true,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Campo que se debe enfocar cuando se abra el formulario.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function focusOnOpen(): string
|
||||
{
|
||||
return 'code';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define reglas de validación dinámicas basadas en el modo actual.
|
||||
*
|
||||
* @param string $mode El modo actual del formulario ('create', 'edit', 'delete').
|
||||
* @return array
|
||||
*/
|
||||
protected function dynamicRules(string $mode): array
|
||||
{
|
||||
switch ($mode) {
|
||||
case 'create':
|
||||
case 'edit':
|
||||
return [
|
||||
'store_id' => ['required', 'integer', 'exists:stores,id'],
|
||||
'work_center_id' => ['nullable', 'integer', 'exists:store_work_centers,id'],
|
||||
'code' => ['required', 'string', 'max:16', Rule::unique('warehouses', 'code')->ignore($this->id)],
|
||||
'name' => ['required', 'string', 'max:96'],
|
||||
'description' => ['nullable', 'string', 'max:1024'],
|
||||
'manager_id' => ['nullable', 'integer', 'exists:users,id'],
|
||||
'tel' => ['nullable', 'regex:/^[0-9+\-\s]+$/', 'max:20'],
|
||||
'tel2' => ['nullable', 'regex:/^[0-9+\-\s]+$/', 'max:20'],
|
||||
'priority' => ['nullable', 'numeric', 'between:0,99'],
|
||||
'status' => ['nullable', 'boolean'],
|
||||
];
|
||||
|
||||
case 'delete':
|
||||
return [
|
||||
'confirmDeletion' => 'accepted', // Asegura que el usuario confirme la eliminación
|
||||
];
|
||||
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== VALIDACIONES =====================
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function attributes(): array
|
||||
{
|
||||
return [
|
||||
'code' => 'código de almacén',
|
||||
'name' => 'nombre del almacén',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error messages for the defined validation rules.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function messages(): array
|
||||
{
|
||||
return [
|
||||
'store_id.required' => 'El almacén debe estar asociado a un negocio.',
|
||||
'code.required' => 'El código del almacén es obligatorio.',
|
||||
'code.unique' => 'Este código ya está en uso por otro almacén.',
|
||||
'name.required' => 'El nombre del almacén es obligatorio.',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Carga el formulario con datos del almacén y actualiza las opciones dinámicas.
|
||||
*
|
||||
* @param int $id
|
||||
*/
|
||||
public function loadFormModel($id): void
|
||||
{
|
||||
parent::loadFormModel($id);
|
||||
|
||||
$this->work_center_options = DB::table('store_work_centers')
|
||||
->where('store_id', $this->store_id)
|
||||
->pluck('name', 'id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Carga el formulario para eliminar un almacén, actualizando las opciones necesarias.
|
||||
*
|
||||
* @param int $id
|
||||
*/
|
||||
public function loadFormModelForDeletion($id): void
|
||||
{
|
||||
parent::loadFormModelForDeletion($id);
|
||||
|
||||
$this->work_center_options = DB::table('store_work_centers')
|
||||
->where('store_id', $this->store_id)
|
||||
->pluck('name', 'id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define las opciones de los selectores desplegables.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function options(): array
|
||||
{
|
||||
$storeCatalogService = app(StoreCatalogService::class);
|
||||
$contactCatalogService = app(ContactCatalogService::class);
|
||||
|
||||
return [
|
||||
'store_options' => $storeCatalogService->searchCatalog('stores', '', ['limit' => -1]),
|
||||
'manager_options' => $contactCatalogService->searchCatalog('users', '', ['limit' => -1]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ruta de la vista asociada con este formulario.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function viewPath(): string
|
||||
{
|
||||
return 'vuexy-website-admin::livewire.legal-notices.offcanvas-form';
|
||||
}
|
||||
}
|
109
Livewire/LegalNotices/LegalNoticesIndex.php
Normal file
109
Livewire/LegalNotices/LegalNoticesIndex.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\LegalNotices;
|
||||
|
||||
use Livewire\Component;
|
||||
use Koneko\VuexyAdmin\Rules\NotEmptyHtml;
|
||||
use Koneko\VuexyAdmin\Services\SettingsService;
|
||||
use Koneko\VuexyWebsiteAdmin\Services\WebsiteTemplateService;
|
||||
|
||||
class LegalNoticesIndex extends Component
|
||||
{
|
||||
private $targetNotify = "#website-legal-settings-card .notification-container";
|
||||
|
||||
public $legalVars = [];
|
||||
public $currentSection = null;
|
||||
|
||||
protected $listeners = [
|
||||
'saveLegalNotices' => '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());
|
||||
|
||||
$SettingsService = app(SettingsService::class);
|
||||
|
||||
$SettingsService->set($this->currentSection . '_enabled', $this->legalVars[$this->currentSection]['enabled'], null, 'vuexy-website-admin');
|
||||
$SettingsService->set($this->currentSection . '_content', $this->legalVars[$this->currentSection]['content'], null, 'vuexy-website-admin');
|
||||
|
||||
$this->dispatch(
|
||||
'notification',
|
||||
target: $this->targetNotify,
|
||||
type: 'success',
|
||||
message: 'Se han guardado los cambios en las configuraciones.'
|
||||
);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('vuexy-website-admin::livewire.legal-notices.index');
|
||||
}
|
||||
}
|
108
Livewire/LegalNotices/LegalSettings.php
Normal file
108
Livewire/LegalNotices/LegalSettings.php
Normal 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');
|
||||
}
|
||||
}
|
38
Livewire/SitemapManager/SitemapManagerIndex.php
Normal file
38
Livewire/SitemapManager/SitemapManagerIndex.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\SitemapManager;
|
||||
|
||||
use Livewire\Component;
|
||||
use Koneko\VuexyWebsiteAdmin\Models\SitemapUrl;
|
||||
|
||||
class SitemapManagerIndex extends Component
|
||||
{
|
||||
public $urls, $newUrl, $changefreq = 'weekly', $priority = 0.5;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->urls = SitemapUrl::all();
|
||||
}
|
||||
|
||||
public function addUrl()
|
||||
{
|
||||
SitemapUrl::create([
|
||||
'url' => $this->newUrl,
|
||||
'changefreq' => $this->changefreq,
|
||||
'priority' => $this->priority,
|
||||
'lastmod' => now()
|
||||
]);
|
||||
$this->reset(['newUrl', 'changefreq', 'priority']);
|
||||
$this->mount();
|
||||
}
|
||||
|
||||
public function deleteUrl($id)
|
||||
{
|
||||
SitemapUrl::find($id)->delete();
|
||||
$this->mount();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('vuexy-website-admin::livewire.sitemap-manager.index', ['urls' => $this->urls]);
|
||||
}}
|
217
Livewire/SitemapManager/SitemapUrlOffcanvasForm.php
Normal file
217
Livewire/SitemapManager/SitemapUrlOffcanvasForm.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\SitemapManager;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Koneko\VuexyAdmin\Livewire\Form\AbstractFormOffCanvasComponent;
|
||||
use Koneko\VuexyContacts\Services\ContactCatalogService;
|
||||
use Koneko\VuexyStoreManager\Services\StoreCatalogService;
|
||||
use Koneko\VuexyWarehouse\Models\Warehouse;
|
||||
|
||||
/**
|
||||
* Class SitemapUrlOffcanvasForm
|
||||
*
|
||||
* Componente Livewire para gestionar almacenes.
|
||||
* Extiende la clase AbstractFormOffCanvasComponent e implementa validaciones dinámicas,
|
||||
* manejo de formularios, eventos y actualizaciones en tiempo real.
|
||||
*
|
||||
* @package Koneko\VuexyWarehouse\Livewire\Warehouses
|
||||
*/
|
||||
class SitemapUrlOffcanvasForm extends AbstractFormOffCanvasComponent
|
||||
{
|
||||
/**
|
||||
* Propiedades del formulario relacionadas con el almacén.
|
||||
*/
|
||||
public $id, $store_id, $work_center_id, $code, $name, $description,
|
||||
$manager_id, $tel, $tel2, $priority, $status, $confirmDeletion;
|
||||
|
||||
/**
|
||||
* Listas de opciones para selects en el formulario.
|
||||
*/
|
||||
public $store_options = [],
|
||||
$work_center_options = [],
|
||||
$manager_options = [];
|
||||
|
||||
/**
|
||||
* Eventos de escucha de Livewire.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $listeners = [
|
||||
'editWarehouse' => 'loadFormModel',
|
||||
'confirmDeletionWarehouse' => 'loadFormModelForDeletion',
|
||||
];
|
||||
|
||||
/**
|
||||
* Definición de tipos de datos que se deben castear.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'status' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Define el modelo Eloquent asociado con el formulario.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function model(): string
|
||||
{
|
||||
return Warehouse::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define los campos del formulario.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function fields(): array
|
||||
{
|
||||
return (new Warehouse())->getFillable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Valores por defecto para el formulario.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function defaults(): array
|
||||
{
|
||||
return [
|
||||
'priority' => 0,
|
||||
'status' => true,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Campo que se debe enfocar cuando se abra el formulario.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function focusOnOpen(): string
|
||||
{
|
||||
return 'code';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define reglas de validación dinámicas basadas en el modo actual.
|
||||
*
|
||||
* @param string $mode El modo actual del formulario ('create', 'edit', 'delete').
|
||||
* @return array
|
||||
*/
|
||||
protected function dynamicRules(string $mode): array
|
||||
{
|
||||
switch ($mode) {
|
||||
case 'create':
|
||||
case 'edit':
|
||||
return [
|
||||
'store_id' => ['required', 'integer', 'exists:stores,id'],
|
||||
'work_center_id' => ['nullable', 'integer', 'exists:store_work_centers,id'],
|
||||
'code' => ['required', 'string', 'max:16', Rule::unique('warehouses', 'code')->ignore($this->id)],
|
||||
'name' => ['required', 'string', 'max:96'],
|
||||
'description' => ['nullable', 'string', 'max:1024'],
|
||||
'manager_id' => ['nullable', 'integer', 'exists:users,id'],
|
||||
'tel' => ['nullable', 'regex:/^[0-9+\-\s]+$/', 'max:20'],
|
||||
'tel2' => ['nullable', 'regex:/^[0-9+\-\s]+$/', 'max:20'],
|
||||
'priority' => ['nullable', 'numeric', 'between:0,99'],
|
||||
'status' => ['nullable', 'boolean'],
|
||||
];
|
||||
|
||||
case 'delete':
|
||||
return [
|
||||
'confirmDeletion' => 'accepted', // Asegura que el usuario confirme la eliminación
|
||||
];
|
||||
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== VALIDACIONES =====================
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function attributes(): array
|
||||
{
|
||||
return [
|
||||
'code' => 'código de almacén',
|
||||
'name' => 'nombre del almacén',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error messages for the defined validation rules.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function messages(): array
|
||||
{
|
||||
return [
|
||||
'store_id.required' => 'El almacén debe estar asociado a un negocio.',
|
||||
'code.required' => 'El código del almacén es obligatorio.',
|
||||
'code.unique' => 'Este código ya está en uso por otro almacén.',
|
||||
'name.required' => 'El nombre del almacén es obligatorio.',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Carga el formulario con datos del almacén y actualiza las opciones dinámicas.
|
||||
*
|
||||
* @param int $id
|
||||
*/
|
||||
public function loadFormModel($id): void
|
||||
{
|
||||
parent::loadFormModel($id);
|
||||
|
||||
$this->work_center_options = DB::table('store_work_centers')
|
||||
->where('store_id', $this->store_id)
|
||||
->pluck('name', 'id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Carga el formulario para eliminar un almacén, actualizando las opciones necesarias.
|
||||
*
|
||||
* @param int $id
|
||||
*/
|
||||
public function loadFormModelForDeletion($id): void
|
||||
{
|
||||
parent::loadFormModelForDeletion($id);
|
||||
|
||||
$this->work_center_options = DB::table('store_work_centers')
|
||||
->where('store_id', $this->store_id)
|
||||
->pluck('name', 'id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define las opciones de los selectores desplegables.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function options(): array
|
||||
{
|
||||
$storeCatalogService = app(StoreCatalogService::class);
|
||||
$contactCatalogService = app(ContactCatalogService::class);
|
||||
|
||||
return [
|
||||
'store_options' => $storeCatalogService->searchCatalog('stores', '', ['limit' => -1]),
|
||||
'manager_options' => $contactCatalogService->searchCatalog('users', '', ['limit' => -1]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ruta de la vista asociada con este formulario.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function viewPath(): string
|
||||
{
|
||||
return 'vuexy-website-admin::livewire.sitemap-manager.offcanvas-form';
|
||||
}
|
||||
}
|
67
Livewire/VuexyWebsiteAdmin/ChatSettings.php
Normal file
67
Livewire/VuexyWebsiteAdmin/ChatSettings.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\VuexyWebsiteAdmin;
|
||||
|
||||
use Koneko\VuexyAdmin\Services\SettingsService;
|
||||
use Koneko\VuexyWebsiteAdmin\Services\WebsiteTemplateService;
|
||||
use Livewire\Component;
|
||||
|
||||
class ChatSettings extends Component
|
||||
{
|
||||
private $targetNotify = "#website-chat-settings-card .notification-container";
|
||||
|
||||
public $chat_provider,
|
||||
$chat_whatsapp_number,
|
||||
$chat_whatsapp_message;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->resetForm();
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
if ($this->chat_provider == 'whatsapp') {
|
||||
$this->validate([
|
||||
'chat_whatsapp_number' => 'required|string|max:20',
|
||||
'chat_whatsapp_message' => 'required|string|max:255',
|
||||
]);
|
||||
}
|
||||
|
||||
// Guardar título del sitio en configuraciones
|
||||
$SettingsService = app(SettingsService::class);
|
||||
|
||||
$SettingsService->set('chat.provider', $this->chat_provider, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('chat.whatsapp_number', $this->chat_whatsapp_number, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('chat.whatsapp_message', $this->chat_whatsapp_message, null, 'vuexy-website-admin');
|
||||
|
||||
// Limpiar cache de plantilla
|
||||
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
|
||||
|
||||
// Recargamos el formulario
|
||||
$this->resetForm();
|
||||
|
||||
// Notificación de éxito
|
||||
$this->dispatch(
|
||||
'notification',
|
||||
target: $this->targetNotify,
|
||||
type: 'success',
|
||||
message: 'Se han guardado los cambios en las configuraciones.'
|
||||
);
|
||||
}
|
||||
|
||||
public function resetForm()
|
||||
{
|
||||
// Obtener los valores de las configuraciones de la base de datos
|
||||
$settings = app(WebsiteTemplateService::class)->getWebsiteVars('chat');
|
||||
|
||||
$this->chat_provider = $settings['provider'];
|
||||
$this->chat_whatsapp_number = $settings['whatsapp_number'];
|
||||
$this->chat_whatsapp_message = $settings['whatsapp_message'];
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('vuexy-website-admin::livewire.vuexy.chat-settings');
|
||||
}
|
||||
}
|
70
Livewire/VuexyWebsiteAdmin/ContactFormSettings.php
Normal file
70
Livewire/VuexyWebsiteAdmin/ContactFormSettings.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\VuexyWebsiteAdmin;
|
||||
|
||||
use Koneko\VuexyAdmin\Services\SettingsService;
|
||||
use Koneko\VuexyWebsiteAdmin\Services\WebsiteTemplateService;
|
||||
use Livewire\Component;
|
||||
|
||||
class ContactFormSettings extends Component
|
||||
{
|
||||
private $targetNotify = "#website-contact-form-settings-card .notification-container";
|
||||
|
||||
public $to_email,
|
||||
$to_email_cc,
|
||||
$subject,
|
||||
$submit_message;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->resetForm();
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->validate([
|
||||
'to_email' => 'required|email',
|
||||
'to_email_cc' => 'nullable|email',
|
||||
'subject' => 'required|string',
|
||||
'submit_message' => 'required|string'
|
||||
]);
|
||||
|
||||
// Guardar título del sitio en configuraciones
|
||||
$SettingsService = app(SettingsService::class);
|
||||
|
||||
$SettingsService->set('contact.form.to_email', $this->to_email, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('contact.form.to_email_cc', $this->to_email_cc, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('contact.form.subject', $this->subject, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('contact.form.submit_message', $this->submit_message, null, 'vuexy-website-admin');
|
||||
|
||||
// Limpiar cache de plantilla
|
||||
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
|
||||
|
||||
// Recargamos el formulario
|
||||
$this->resetForm();
|
||||
|
||||
// Notificación de éxito
|
||||
$this->dispatch(
|
||||
'notification',
|
||||
target: $this->targetNotify,
|
||||
type: 'success',
|
||||
message: 'Se han guardado los cambios en las configuraciones.'
|
||||
);
|
||||
}
|
||||
|
||||
public function resetForm()
|
||||
{
|
||||
// Obtener los valores de las configuraciones de la base de datos
|
||||
$settings = app(WebsiteTemplateService::class)->getWebsiteVars('contact');
|
||||
|
||||
$this->to_email = $settings['form']['to_email'];
|
||||
$this->to_email_cc = $settings['form']['to_email_cc'];
|
||||
$this->subject = $settings['form']['subject'];
|
||||
$this->submit_message = $settings['form']['submit_message'];
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('vuexy-website-admin::livewire.vuexy.contact-form-settings');
|
||||
}
|
||||
}
|
78
Livewire/VuexyWebsiteAdmin/ContactInfoSettings.php
Normal file
78
Livewire/VuexyWebsiteAdmin/ContactInfoSettings.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\VuexyWebsiteAdmin;
|
||||
|
||||
use Koneko\VuexyAdmin\Services\SettingsService;
|
||||
use Koneko\VuexyWebsiteAdmin\Services\WebsiteTemplateService;
|
||||
use Livewire\Component;
|
||||
|
||||
class ContactInfoSettings extends Component
|
||||
{
|
||||
private $targetNotify = "#website-contact-info-settings-card .notification-container";
|
||||
|
||||
public $phone_number,
|
||||
$phone_number_ext,
|
||||
$phone_number_2,
|
||||
$phone_number_2_ext,
|
||||
$email,
|
||||
$horario;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->resetForm();
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->validate([
|
||||
'phone_number' => 'nullable|string',
|
||||
'phone_number_ext' => 'nullable|string',
|
||||
'phone_number_2' => 'nullable|string',
|
||||
'phone_number_2_ext' => 'nullable|string',
|
||||
'email' => 'nullable|email',
|
||||
'horario' => 'nullable|string',
|
||||
]);
|
||||
|
||||
// Guardar título del sitio en configuraciones
|
||||
$SettingsService = app(SettingsService::class);
|
||||
|
||||
$SettingsService->set('contact.phone_number', $this->phone_number, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('contact.phone_number_ext', $this->phone_number_ext, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('contact.phone_number_2', $this->phone_number_2, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('contact.phone_number_2_ext', $this->phone_number_2_ext, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('contact.email', $this->email, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('contact.horario', $this->horario, null, 'vuexy-website-admin');
|
||||
|
||||
// Limpiar cache de plantilla
|
||||
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
|
||||
|
||||
// Recargamos el formulario
|
||||
$this->resetForm();
|
||||
|
||||
// Notificación de éxito
|
||||
$this->dispatch(
|
||||
'notification',
|
||||
target: $this->targetNotify,
|
||||
type: 'success',
|
||||
message: 'Se han guardado los cambios en las configuraciones.'
|
||||
);
|
||||
}
|
||||
|
||||
public function resetForm()
|
||||
{
|
||||
// Obtener los valores de las configuraciones de la base de datos
|
||||
$settings = app(WebsiteTemplateService::class)->getWebsiteVars('contact');
|
||||
|
||||
$this->phone_number = $settings['phone_number'];
|
||||
$this->phone_number_ext = $settings['phone_number_ext'];
|
||||
$this->phone_number_2 = $settings['phone_number_2'];
|
||||
$this->phone_number_2_ext = $settings['phone_number_2_ext'];
|
||||
$this->email = $settings['email'];
|
||||
$this->horario = $settings['horario'];
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('vuexy-website-admin::livewire.vuexy.contact-info-settings');
|
||||
}
|
||||
}
|
63
Livewire/VuexyWebsiteAdmin/GoogleAnalyticsSettings.php
Normal file
63
Livewire/VuexyWebsiteAdmin/GoogleAnalyticsSettings.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\VuexyWebsiteAdmin;
|
||||
|
||||
use Koneko\VuexyAdmin\Services\SettingsService;
|
||||
use Koneko\VuexyWebsiteAdmin\Services\WebsiteTemplateService;
|
||||
use Livewire\Component;
|
||||
|
||||
class GoogleAnalyticsSettings extends Component
|
||||
{
|
||||
private $targetNotify = "#website-analytics-settings-card .notification-container";
|
||||
|
||||
public $google_analytics_enabled,
|
||||
$google_analytics_id;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->resetForm();
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
if ($this->google_analytics_enabled) {
|
||||
$this->validate([
|
||||
'google_analytics_id' => 'required|string|min:12|max:30',
|
||||
]);
|
||||
}
|
||||
|
||||
// Guardar título del sitio en configuraciones
|
||||
$SettingsService = app(SettingsService::class);
|
||||
|
||||
$SettingsService->set('google.analytics_enabled', $this->google_analytics_enabled, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('google.analytics_id', $this->google_analytics_id, null, 'vuexy-website-admin');
|
||||
|
||||
// Limpiar cache de plantilla
|
||||
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
|
||||
|
||||
// Recargamos el formulario
|
||||
$this->resetForm();
|
||||
|
||||
// Notificación de éxito
|
||||
$this->dispatch(
|
||||
'notification',
|
||||
target: $this->targetNotify,
|
||||
type: 'success',
|
||||
message: 'Se han guardado los cambios en las configuraciones.'
|
||||
);
|
||||
}
|
||||
|
||||
public function resetForm()
|
||||
{
|
||||
// Obtener los valores de las configuraciones de la base de datos
|
||||
$settings = app(WebsiteTemplateService::class)->getWebsiteVars('google');
|
||||
|
||||
$this->google_analytics_enabled = $settings['analytics']['enabled'];
|
||||
$this->google_analytics_id = $settings['analytics']['id'];
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('vuexy-website-admin::livewire.vuexy.analytics-settings');
|
||||
}
|
||||
}
|
69
Livewire/VuexyWebsiteAdmin/LocationSettings.php
Normal file
69
Livewire/VuexyWebsiteAdmin/LocationSettings.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\VuexyWebsiteAdmin;
|
||||
|
||||
use Koneko\VuexyAdmin\Services\SettingsService;
|
||||
use Koneko\VuexyWebsiteAdmin\Services\WebsiteTemplateService;
|
||||
use Livewire\Component;
|
||||
|
||||
class LocationSettings extends Component
|
||||
{
|
||||
private $targetNotify = "#website-location-settings-card .notification-container";
|
||||
|
||||
public $direccion,
|
||||
$location_lat,
|
||||
$location_lng;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->resetForm();
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->validate([
|
||||
'direccion' => ['nullable', 'string', 'max:255'],
|
||||
'location_lat' => ['nullable', 'numeric'],
|
||||
'location_lng' => ['nullable', 'numeric'],
|
||||
]);
|
||||
|
||||
// Guardar título del sitio en configuraciones
|
||||
$SettingsService = app(SettingsService::class);
|
||||
|
||||
$location_lat = $this->location_lat? (float) $this->location_lat: null;
|
||||
$location_lng = $this->location_lng? (float) $this->location_lng: null;
|
||||
|
||||
$SettingsService->set('contact.direccion', $this->direccion, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('contact.location.lat', $location_lat, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('contact.location.lng', $location_lng, null, 'vuexy-website-admin');
|
||||
|
||||
// Limpiar cache de plantilla
|
||||
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
|
||||
|
||||
// Recargamos el formulario
|
||||
$this->resetForm();
|
||||
|
||||
// Notificación de éxito
|
||||
$this->dispatch(
|
||||
'notification',
|
||||
target: $this->targetNotify,
|
||||
type: 'success',
|
||||
message: 'Se han guardado los cambios en las configuraciones.'
|
||||
);
|
||||
}
|
||||
|
||||
public function resetForm()
|
||||
{
|
||||
// Obtener los valores de las configuraciones de la base de datos
|
||||
$settings = app(WebsiteTemplateService::class)->getWebsiteVars('contact');
|
||||
|
||||
$this->direccion = $settings['direccion'];
|
||||
$this->location_lat = $settings['location']['lat'];
|
||||
$this->location_lng = $settings['location']['lng'];
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('vuexy-website-admin::livewire.vuexy.location-settings');
|
||||
}
|
||||
}
|
61
Livewire/VuexyWebsiteAdmin/LogoOnDarkBgSettings.php
Normal file
61
Livewire/VuexyWebsiteAdmin/LogoOnDarkBgSettings.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\VuexyWebsiteAdmin;
|
||||
|
||||
use Livewire\Component;
|
||||
use Livewire\WithFileUploads;
|
||||
use Koneko\VuexyWebsiteAdmin\Services\WebsiteSettingsService;
|
||||
use Koneko\VuexyWebsiteAdmin\Services\WebsiteTemplateService;
|
||||
|
||||
class LogoOnDarkBgSettings extends Component
|
||||
{
|
||||
use WithFileUploads;
|
||||
|
||||
private $targetNotify = "#logo-on-dark-bg-settings-card .notification-container";
|
||||
|
||||
public $website_image_logo_dark,
|
||||
$upload_image_logo_dark;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->resetForm();
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->validate([
|
||||
'upload_image_logo_dark' => 'required|image|mimes:jpeg,png,jpg,svg,webp|max:20480',
|
||||
]);
|
||||
|
||||
// Procesar favicon si se ha cargado una imagen
|
||||
app(WebsiteSettingsService::class)->processAndSaveImageLogo($this->upload_image_logo_dark, 'dark');
|
||||
|
||||
// Limpiar cache de plantilla
|
||||
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
|
||||
|
||||
// Recargamos el formulario
|
||||
$this->resetForm();
|
||||
|
||||
// Notificación de éxito
|
||||
$this->dispatch(
|
||||
'notification',
|
||||
target: $this->targetNotify,
|
||||
type: 'success',
|
||||
message: 'Se han guardado los cambios en las configuraciones.'
|
||||
);
|
||||
}
|
||||
|
||||
public function resetForm()
|
||||
{
|
||||
// Obtener los valores de las configuraciones de la base de datos
|
||||
$settings = app(WebsiteTemplateService::class)->getWebsiteVars();
|
||||
|
||||
$this->upload_image_logo_dark = null;
|
||||
$this->website_image_logo_dark = $settings['image_logo']['large_dark'];
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('vuexy-website-admin::livewire.vuexy.logo-on-dark-bg-settings');
|
||||
}
|
||||
}
|
61
Livewire/VuexyWebsiteAdmin/LogoOnLightBgSettings.php
Normal file
61
Livewire/VuexyWebsiteAdmin/LogoOnLightBgSettings.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\VuexyWebsiteAdmin;
|
||||
|
||||
use Livewire\Component;
|
||||
use Livewire\WithFileUploads;
|
||||
use Koneko\VuexyWebsiteAdmin\Services\WebsiteSettingsService;
|
||||
use Koneko\VuexyWebsiteAdmin\Services\WebsiteTemplateService;
|
||||
|
||||
class LogoOnLightBgSettings extends Component
|
||||
{
|
||||
use WithFileUploads;
|
||||
|
||||
private $targetNotify = "#logo-on-light-bg-settings-card .notification-container";
|
||||
|
||||
public $website_image_logo,
|
||||
$upload_image_logo;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->resetForm();
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->validate([
|
||||
'upload_image_logo' => 'required|image|mimes:jpeg,png,jpg,svg,webp|max:20480',
|
||||
]);
|
||||
|
||||
// Procesar favicon si se ha cargado una imagen
|
||||
app(WebsiteSettingsService::class)->processAndSaveImageLogo($this->upload_image_logo);
|
||||
|
||||
// Limpiar cache de plantilla
|
||||
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
|
||||
|
||||
// Recargamos el formulario
|
||||
$this->resetForm();
|
||||
|
||||
// Notificación de éxito
|
||||
$this->dispatch(
|
||||
'notification',
|
||||
target: $this->targetNotify,
|
||||
type: 'success',
|
||||
message: 'Se han guardado los cambios en las configuraciones.'
|
||||
);
|
||||
}
|
||||
|
||||
public function resetForm()
|
||||
{
|
||||
// Obtener los valores de las configuraciones de la base de datos
|
||||
$settings = app(WebsiteTemplateService::class)->getWebsiteVars();
|
||||
|
||||
$this->upload_image_logo = null;
|
||||
$this->website_image_logo = $settings['image_logo']['large'];
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('vuexy-website-admin::livewire.vuexy.logo-on-light-bg-settings');
|
||||
}
|
||||
}
|
98
Livewire/VuexyWebsiteAdmin/SocialMediaSettings.php
Normal file
98
Livewire/VuexyWebsiteAdmin/SocialMediaSettings.php
Normal file
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\VuexyWebsiteAdmin;
|
||||
|
||||
use Koneko\VuexyAdmin\Services\SettingsService;
|
||||
use Koneko\VuexyWebsiteAdmin\Services\WebsiteTemplateService;
|
||||
use Livewire\Component;
|
||||
|
||||
class SocialMediaSettings 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;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->resetForm();
|
||||
}
|
||||
|
||||
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',
|
||||
]);
|
||||
|
||||
// Guardar título del sitio en configuraciones
|
||||
$SettingsService = app(SettingsService::class);
|
||||
|
||||
$SettingsService->set('social.whatsapp', $this->social_whatsapp, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('social.whatsapp_message', $this->social_whatsapp_message, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('social.facebook', $this->social_facebook, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('social.instagram', $this->social_instagram, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('social.linkedin', $this->social_linkedin, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('social.tiktok', $this->social_tiktok, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('social.x_twitter', $this->social_x_twitter, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('social.google', $this->social_google, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('social.pinterest', $this->social_pinterest, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('social.youtube', $this->social_youtube, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('social.vimeo', $this->social_vimeo, null, 'vuexy-website-admin');
|
||||
|
||||
// Limpiar cache de plantilla
|
||||
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
|
||||
|
||||
// Recargamos el formulario
|
||||
$this->resetForm();
|
||||
|
||||
// Notificación de éxito
|
||||
$this->dispatch(
|
||||
'notification',
|
||||
target: $this->targetNotify,
|
||||
type: 'success',
|
||||
message: 'Se han guardado los cambios en las configuraciones.'
|
||||
);
|
||||
}
|
||||
|
||||
public function resetForm()
|
||||
{
|
||||
// Obtener los valores de las configuraciones de la base de datos
|
||||
$settings = app(WebsiteTemplateService::class)->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 render()
|
||||
{
|
||||
return view('vuexy-website-admin::livewire.vuexy.social-media-settings');
|
||||
}
|
||||
}
|
62
Livewire/VuexyWebsiteAdmin/WebsiteDescriptionSettings.php
Normal file
62
Livewire/VuexyWebsiteAdmin/WebsiteDescriptionSettings.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\VuexyWebsiteAdmin;
|
||||
|
||||
use Livewire\Component;
|
||||
use Koneko\VuexyAdmin\Services\SettingsService;
|
||||
use Koneko\VuexyWebsiteAdmin\Services\WebsiteTemplateService;
|
||||
|
||||
class WebsiteDescriptionSettings extends Component
|
||||
{
|
||||
private $targetNotify = "#website-description-settings-card .notification-container";
|
||||
|
||||
public $title,
|
||||
$description;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->resetForm();
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->validate([
|
||||
'title' => 'required|string|max:255',
|
||||
'description' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
// Guardar título del sitio en configuraciones
|
||||
$SettingsService = app(SettingsService::class);
|
||||
|
||||
$SettingsService->set('website.title', $this->title, null, 'vuexy-website-admin');
|
||||
$SettingsService->set('website.description', $this->description, null, 'vuexy-website-admin');
|
||||
|
||||
// Limpiar cache de plantilla
|
||||
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
|
||||
|
||||
// Recargamos el formulario
|
||||
$this->resetForm();
|
||||
|
||||
// Notificación de éxito
|
||||
$this->dispatch(
|
||||
'notification',
|
||||
target: $this->targetNotify,
|
||||
type: 'success',
|
||||
message: 'Se han guardado los cambios en las configuraciones.'
|
||||
);
|
||||
}
|
||||
|
||||
public function resetForm()
|
||||
{
|
||||
// Obtener los valores de las configuraciones de la base de datos
|
||||
$settings = app(WebsiteTemplateService::class)->getWebsiteVars();
|
||||
|
||||
$this->title = $settings['title'];
|
||||
$this->description = $settings['description'];
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('vuexy-website-admin::livewire.vuexy.website-description-settings');
|
||||
}
|
||||
}
|
72
Livewire/VuexyWebsiteAdmin/WebsiteFaviconSettings.php
Normal file
72
Livewire/VuexyWebsiteAdmin/WebsiteFaviconSettings.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Livewire\VuexyWebsiteAdmin;
|
||||
|
||||
use Livewire\Component;
|
||||
use Livewire\WithFileUploads;
|
||||
use Koneko\VuexyWebsiteAdmin\Services\WebsiteSettingsService;
|
||||
use Koneko\VuexyWebsiteAdmin\Services\WebsiteTemplateService;
|
||||
|
||||
class WebsiteFaviconSettings extends Component
|
||||
{
|
||||
use WithFileUploads;
|
||||
|
||||
private $targetNotify = "#website-favicon-settings-card .notification-container";
|
||||
|
||||
public $website_favicon_16x16,
|
||||
$website_favicon_76x76,
|
||||
$website_favicon_120x120,
|
||||
$website_favicon_152x152,
|
||||
$website_favicon_180x180,
|
||||
$website_favicon_192x192;
|
||||
|
||||
public $upload_image_favicon;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->resetForm();
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->validate([
|
||||
'upload_image_favicon' => 'required|image|mimes:jpeg,png,jpg,svg,webp|max:20480',
|
||||
]);
|
||||
|
||||
// Procesar favicon
|
||||
app(WebsiteSettingsService::class)->processAndSaveFavicon($this->upload_image_favicon);
|
||||
|
||||
// Limpiar cache de plantilla
|
||||
app(WebsiteTemplateService::class)->clearWebsiteVarsCache();
|
||||
|
||||
// Recargamos el formulario
|
||||
$this->resetForm();
|
||||
|
||||
// Notificación de éxito
|
||||
$this->dispatch(
|
||||
'notification',
|
||||
target: $this->targetNotify,
|
||||
type: 'success',
|
||||
message: 'Se han guardado los cambios en las configuraciones.'
|
||||
);
|
||||
}
|
||||
|
||||
public function resetForm()
|
||||
{
|
||||
// Obtener los valores de las configuraciones de la base de datos
|
||||
$settings = app(WebsiteTemplateService::class)->getWebsiteVars();
|
||||
|
||||
$this->upload_image_favicon = null;
|
||||
$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 render()
|
||||
{
|
||||
return view('vuexy-website-admin::livewire.vuexy.website-favicon-settings');
|
||||
}
|
||||
}
|
33
Models/Faq.php
Normal file
33
Models/Faq.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Faq extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'category_id',
|
||||
'question',
|
||||
'answer',
|
||||
'order',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'order' => 'integer',
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Categoría a la que pertenece esta FAQ.
|
||||
*/
|
||||
public function category(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(FaqCategory::class, 'category_id');
|
||||
}
|
||||
}
|
32
Models/FaqCategory.php
Normal file
32
Models/FaqCategory.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class FaqCategory extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'icon',
|
||||
'order',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'order' => 'integer',
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* FAQs asociadas a esta categoría.
|
||||
*/
|
||||
public function faqs(): HasMany
|
||||
{
|
||||
return $this->hasMany(Faq::class, 'category_id');
|
||||
}
|
||||
}
|
0
Models/SitemapConfiguration.php
Normal file
0
Models/SitemapConfiguration.php
Normal file
19
Models/SitemapUrl.php
Normal file
19
Models/SitemapUrl.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SitemapUrl extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'url',
|
||||
'changefreq',
|
||||
'priority',
|
||||
'lastmod',
|
||||
'is_active',
|
||||
];
|
||||
}
|
@ -4,7 +4,16 @@ namespace Koneko\VuexyWebsiteAdmin\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Livewire\Livewire;
|
||||
use OwenIt\Auditing\AuditableObserver;
|
||||
use Koneko\VuexyWebsiteAdmin\Console\Commands\SitemapGenerate;
|
||||
|
||||
use Koneko\VuexyWebsiteAdmin\Livewire\Faq\{FaqIndex,FaqOffCanvasForm};
|
||||
use Koneko\VuexyWebsiteAdmin\Livewire\Images\ImagesIndex;
|
||||
use Koneko\VuexyWebsiteAdmin\Livewire\LegalNotices\{LegalNoticesIndex,LegalNoticeOffCanvasForm};
|
||||
use Koneko\VuexyWebsiteAdmin\Livewire\SitemapManager\{SitemapManagerIndex,SitemapUrlOffcanvasForm};
|
||||
|
||||
use Koneko\VuexyWebsiteAdmin\Livewire\VuexyWebsiteAdmin\{WebsiteDescriptionSettings,WebsiteFaviconSettings,LogoOnLightBgSettings,LogoOnDarkBgSettings};
|
||||
use Koneko\VuexyWebsiteAdmin\Livewire\VuexyWebsiteAdmin\{SocialMediaSettings,ChatSettings,GoogleAnalyticsSettings};
|
||||
use Koneko\VuexyWebsiteAdmin\Livewire\VuexyWebsiteAdmin\{ContactInfoSettings,LocationSettings,ContactFormSettings};
|
||||
|
||||
class VuexyWebsiteAdminServiceProvider extends ServiceProvider
|
||||
{
|
||||
@ -32,18 +41,57 @@ class VuexyWebsiteAdminServiceProvider extends ServiceProvider
|
||||
// Register the migrations
|
||||
$this->loadMigrationsFrom(__DIR__ . '/../database/migrations');
|
||||
|
||||
// Registrar comandos de consola
|
||||
if ($this->app->runningInConsole()) {
|
||||
$this->commands([
|
||||
SitemapGenerate::class,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
// Registrar Livewire Components
|
||||
$components = [
|
||||
//'user-count' => UserCount::class,
|
||||
// ajustes generales
|
||||
'vuexy-website-admin::website-description-settings' => WebsiteDescriptionSettings::class,
|
||||
'vuexy-website-admin::website-favicon-settings' => WebsiteFaviconSettings::class,
|
||||
'vuexy-website-admin::logo-on-light-bg-settings' => LogoOnLightBgSettings::class,
|
||||
'vuexy-website-admin::logo-on-dark-bg-settings' => LogoOnDarkBgSettings::class,
|
||||
|
||||
// Avisos legales
|
||||
'vuexy-website-admin::legal-notices-index' => LegalNoticesIndex::class,
|
||||
'vuexy-website-admin::legal-notice-offcanvas-form' => LegalNoticeOffCanvasForm::class,
|
||||
|
||||
// Preguntas frecuentes
|
||||
'vuexy-website-admin::faq-index' => FaqIndex::class,
|
||||
'vuexy-website-admin::faq-offcanvas-form' => FaqOffCanvasForm::class,
|
||||
|
||||
// Redes sociales
|
||||
'vuexy-website-admin::social-media-settings' => SocialMediaSettings::class,
|
||||
|
||||
// Chat
|
||||
'vuexy-website-admin::chat-settings' => ChatSettings::class,
|
||||
|
||||
// Galería de imágenes
|
||||
'vuexy-website-admin::images-index' => ImagesIndex::class,
|
||||
|
||||
// Google Analytics
|
||||
'vuexy-website-admin::google-analytics-settings' => GoogleAnalyticsSettings::class,
|
||||
|
||||
// Información de contacto
|
||||
'vuexy-website-admin::contact-info-settings' => ContactInfoSettings::class,
|
||||
'vuexy-website-admin::location-settings' => LocationSettings::class,
|
||||
|
||||
// Formulario de contacto
|
||||
'vuexy-website-admin::contact-form-settings' => ContactFormSettings::class,
|
||||
|
||||
// Mapa del sitio
|
||||
'vuexy-website-admin::sitemap-manager-index' => SitemapManagerIndex::class,
|
||||
'vuexy-website-admin::sitemap-manager-offcanvas-form' => SitemapUrlOffcanvasForm::class,
|
||||
];
|
||||
|
||||
foreach ($components as $alias => $component) {
|
||||
Livewire::component($alias, $component);
|
||||
}
|
||||
|
||||
|
||||
// Registrar auditoría en usuarios
|
||||
//User::observe(AuditableObserver::class);
|
||||
}
|
||||
}
|
||||
|
292
Services/WebsiteSettingsService.php
Normal file
292
Services/WebsiteSettingsService.php
Normal file
@ -0,0 +1,292 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Intervention\Image\ImageManager;
|
||||
use Koneko\VuexyAdmin\Models\Setting;
|
||||
use Koneko\VuexyAdmin\Services\SettingsService;
|
||||
|
||||
/**
|
||||
* Servicio para gestionar la configuración del template del website.
|
||||
*
|
||||
* Este servicio maneja el procesamiento y almacenamiento de imágenes del favicon
|
||||
* y logos del website, incluyendo diferentes versiones y tamaños.
|
||||
*
|
||||
* @package Koneko\VuexyWebsiteAdmin\Services
|
||||
*/
|
||||
class WebsiteSettingsService
|
||||
{
|
||||
/** @var string Driver de procesamiento de imágenes */
|
||||
private $driver;
|
||||
|
||||
/** @var string Disco de almacenamiento para imágenes */
|
||||
private $imageDisk = 'public';
|
||||
|
||||
/** @var string Ruta base para favicons */
|
||||
private $favicon_basePath = 'favicon/';
|
||||
|
||||
/** @var string Ruta base para logos */
|
||||
private $image_logo_basePath = 'images/logo/';
|
||||
|
||||
/** @var array<string,array<int>> Tamaños predefinidos para favicons */
|
||||
private $faviconsSizes = [
|
||||
'180x180' => [180, 180],
|
||||
'192x192' => [192, 192],
|
||||
'152x152' => [152, 152],
|
||||
'120x120' => [120, 120],
|
||||
'76x76' => [76, 76],
|
||||
'16x16' => [16, 16],
|
||||
];
|
||||
|
||||
/** @var int Área máxima en píxeles para la primera versión del logo */
|
||||
private $imageLogoMaxPixels1 = 22500;
|
||||
|
||||
/** @var int Área máxima en píxeles para la segunda versión del logo */
|
||||
private $imageLogoMaxPixels2 = 75625;
|
||||
|
||||
/** @var int Área máxima en píxeles para la tercera versión del logo */
|
||||
private $imageLogoMaxPixels3 = 262144;
|
||||
|
||||
/** @var int Área máxima en píxeles para la versión base64 del logo */
|
||||
private $imageLogoMaxPixels4 = 230400;
|
||||
|
||||
/** @var int Tiempo de vida en caché en minutos */
|
||||
protected $cacheTTL = 60 * 24 * 30;
|
||||
|
||||
/**
|
||||
* Constructor del servicio
|
||||
*
|
||||
* Inicializa el driver de procesamiento de imágenes desde la configuración
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->driver = config('image.driver', 'gd');
|
||||
}
|
||||
|
||||
/**
|
||||
* Procesa y guarda un nuevo favicon
|
||||
*
|
||||
* Genera múltiples versiones del favicon en diferentes tamaños predefinidos,
|
||||
* elimina las versiones anteriores y actualiza la configuración.
|
||||
*
|
||||
* @param \Illuminate\Http\UploadedFile $image Archivo de imagen subido
|
||||
* @return void
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
// Actualizar configuración utilizando SettingService
|
||||
$SettingsService = app(SettingsService::class);
|
||||
$SettingsService->set('website.favicon_ns', $this->favicon_basePath . $imageName, null, 'vuexy-website-admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Elimina los favicons antiguos del almacenamiento
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function deleteOldFavicons(): void
|
||||
{
|
||||
// Obtener el favicon actual desde la base de datos
|
||||
$currentFavicon = Setting::where('key', 'website.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Procesa y guarda un nuevo logo
|
||||
*
|
||||
* Genera múltiples versiones del logo con diferentes tamaños máximos,
|
||||
* incluyendo una versión en base64, y actualiza la configuración.
|
||||
*
|
||||
* @param \Illuminate\Http\UploadedFile $image Archivo de imagen subido
|
||||
* @param string $type Tipo de logo ('dark' para modo oscuro, '' para normal)
|
||||
* @return void
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera y guarda una versión del logo
|
||||
*
|
||||
* @param \Intervention\Image\Interfaces\ImageInterface $image Imagen a procesar
|
||||
* @param string $type Tipo de logo ('dark' para modo oscuro, '' para normal)
|
||||
* @param int $maxPixels Área máxima en píxeles
|
||||
* @param string $suffix Sufijo para el nombre del archivo
|
||||
* @return void
|
||||
*/
|
||||
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' : '');
|
||||
$keyValue = '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
|
||||
$SettingsService = app(SettingsService::class);
|
||||
$SettingsService->set($keyValue, $resizedPath, null, 'vuexy-website-admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Redimensiona una imagen manteniendo su proporción
|
||||
*
|
||||
* @param \Intervention\Image\Interfaces\ImageInterface $image Imagen a redimensionar
|
||||
* @param int $maxPixels Área máxima en píxeles
|
||||
* @return \Intervention\Image\Interfaces\ImageInterface
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera y guarda una versión del logo en formato base64
|
||||
*
|
||||
* @param \Intervention\Image\Interfaces\ImageInterface $image Imagen a procesar
|
||||
* @param string $type Tipo de logo ('dark' para modo oscuro, '' para normal)
|
||||
* @param int $maxPixels Área máxima en píxeles
|
||||
* @return void
|
||||
*/
|
||||
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
|
||||
$SettingsService = app(SettingsService::class);
|
||||
$SettingsService->set("website.image.logo_base64" . ($type === 'dark' ? '_dark' : ''), $base64Image, null, 'vuexy-website-admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Elimina las imágenes antiguas del logo
|
||||
*
|
||||
* @param string $type Tipo de logo ('dark' para modo oscuro, '' para normal)
|
||||
* @return void
|
||||
*/
|
||||
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}",
|
||||
];
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
395
Services/WebsiteTemplateService.php
Normal file
395
Services/WebsiteTemplateService.php
Normal file
@ -0,0 +1,395 @@
|
||||
<?php
|
||||
|
||||
namespace Koneko\VuexyWebsiteAdmin\Services;
|
||||
|
||||
use Illuminate\Support\Facades\{Cache,Schema};
|
||||
use Koneko\VuexyAdmin\Models\Setting;
|
||||
|
||||
/**
|
||||
* Servicio para gestionar la configuración y personalización del template del Website.
|
||||
*
|
||||
* Esta clase maneja las configuraciones del template del website, incluyendo variables
|
||||
* de personalización, logos, favicons y otras configuraciones de la interfaz.
|
||||
* Implementa un sistema de caché para optimizar el rendimiento.
|
||||
*/
|
||||
class WebsiteTemplateService
|
||||
{
|
||||
/** @var int Tiempo de vida del caché en minutos (60 * 24 * 30 = 30 días) */
|
||||
protected $cacheTTL = 60 * 24 * 30;
|
||||
|
||||
/**
|
||||
* Obtiene las variables del template del website.
|
||||
*
|
||||
* @param string $setting Clave de la configuración a obtener
|
||||
* @return array Array con las variables del template
|
||||
*/
|
||||
public function getWebsiteVars(string $setting = ''): array
|
||||
{
|
||||
try {
|
||||
// Verifica si la base de datos está inicializada
|
||||
if (!Schema::hasTable('migrations')) {
|
||||
return $this->getDefaultWebsiteVars($setting);
|
||||
}
|
||||
|
||||
$webVars = Cache::remember('website_settings', $this->cacheTTL, function () {
|
||||
$settings = Setting::withVirtualValue()
|
||||
->where(function ($query) {
|
||||
$query->where('key', 'LIKE', 'website.%')
|
||||
->orWhere('key', 'LIKE', 'google.%')
|
||||
->orWhere('key', 'LIKE', 'chat.%');
|
||||
})
|
||||
->pluck('value', 'key')
|
||||
->toArray();
|
||||
|
||||
return $this->buildWebsiteVars($settings);
|
||||
});
|
||||
|
||||
return $setting ? ($webVars[$setting] ?? []) : $webVars;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// Manejo de excepciones: devolver valores predeterminados
|
||||
return $this->getDefaultWebsiteVars($setting);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construye las variables del template del website.
|
||||
*
|
||||
* @param array $settings Array asociativo de configuraciones
|
||||
* @return array Array con las variables del template
|
||||
*/
|
||||
private function buildWebsiteVars(array $settings): array
|
||||
{
|
||||
return [
|
||||
'title' => $settings['website.title'] ?? config('_var.appTitle'),
|
||||
'author' => config('_var.author'),
|
||||
'description' => $settings['website.description'] ?? config('_var.appDescription'),
|
||||
'favicon' => $this->getFaviconPaths($settings),
|
||||
'app_name' => $settings['website.app_name'] ?? config('_var.appName'),
|
||||
'image_logo' => $this->getImageLogoPaths($settings),
|
||||
//'template' => $this->getTemplateVars($settings),
|
||||
'google' => $this->getGoogleVars($settings),
|
||||
'chat' => $this->getChatVars($settings),
|
||||
'contact' => $this->getContactVars(),
|
||||
'social' => $this->getSocialVars(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene las variables del template del website por defecto.
|
||||
*
|
||||
* @param string $setting Clave de la configuración a obtener
|
||||
* @return array Array con las variables del template
|
||||
*/
|
||||
private function getDefaultWebsiteVars(string $setting = ''): array
|
||||
{
|
||||
$defaultVars = [
|
||||
'title' => config('_var.appTitle', 'Default Title'),
|
||||
'author' => config('_var.author', 'Default Author'),
|
||||
'description' => config('_var.appDescription', 'Default Description'),
|
||||
'favicon' => $this->getFaviconPaths([]),
|
||||
'image_logo' => $this->getImageLogoPaths([]),
|
||||
//'template' => $this->getTemplateVars([]),
|
||||
'google' => $this->getGoogleVars([]),
|
||||
'chat' => $this->getChatVars([]),
|
||||
'contact' => [],
|
||||
'social' => [],
|
||||
];
|
||||
|
||||
return $setting ? ($defaultVars[$setting] ?? []) : $defaultVars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera las rutas para los diferentes tamaños de favicon.
|
||||
*
|
||||
* @param array $settings Array asociativo de configuraciones
|
||||
* @return array Array con las rutas de los favicons en diferentes tamaños
|
||||
*/
|
||||
private function getFaviconPaths(array $settings): array
|
||||
{
|
||||
$defaultFavicon = config('koneko.appFavicon');
|
||||
$namespace = $settings['website.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,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera las rutas para los diferentes tamaños y versiones del logo.
|
||||
*
|
||||
* @param array $settings Array asociativo de configuraciones
|
||||
* @return array Array con las rutas de los logos en diferentes tamaños y modos
|
||||
*/
|
||||
private function getImageLogoPaths(array $settings): array
|
||||
{
|
||||
$defaultLogo = config('koneko.appLogo');
|
||||
|
||||
return [
|
||||
'small' => $this->getImagePath($settings, 'website.image.logo_small', $defaultLogo),
|
||||
'medium' => $this->getImagePath($settings, 'website.image.logo_medium', $defaultLogo),
|
||||
'large' => $this->getImagePath($settings, 'website.image.logo', $defaultLogo),
|
||||
'small_dark' => $this->getImagePath($settings, 'website.image.logo_small_dark', $defaultLogo),
|
||||
'medium_dark' => $this->getImagePath($settings, 'website.image.logo_medium_dark', $defaultLogo),
|
||||
'large_dark' => $this->getImagePath($settings, 'website.image.logo_dark', $defaultLogo),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene la ruta de una imagen específica desde las configuraciones.
|
||||
*
|
||||
* @param array $settings Array asociativo de configuraciones
|
||||
* @param string $key Clave de la configuración
|
||||
* @param string $default Valor predeterminado si no se encuentra la configuración
|
||||
* @return string Ruta de la imagen
|
||||
*/
|
||||
private function getImagePath(array $settings, string $key, string $default): string
|
||||
{
|
||||
return $settings[$key] ?? $default;
|
||||
}
|
||||
|
||||
/*
|
||||
private function getTemplateVars(array $settings): array
|
||||
{
|
||||
return [
|
||||
'style_switcher' => (bool)($settings['website.tpl_style_switcher'] ?? false),
|
||||
'footer_text' => $settings['website.tpl_footer_text'] ?? '',
|
||||
];
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Obtiene las variables de Google Analytics.
|
||||
*
|
||||
* @param array $settings Array asociativo de configuraciones
|
||||
* @return array Array con las variables de Google Analytics
|
||||
*/
|
||||
private function getGoogleVars(array $settings): array
|
||||
{
|
||||
return [
|
||||
'analytics' => [
|
||||
'enabled' => (bool)($settings['google.analytics_enabled'] ?? false),
|
||||
'id' => $settings['google.analytics_id'] ?? '',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene las variables de chat.
|
||||
*
|
||||
* @param array $settings Array asociativo de configuraciones
|
||||
* @return array Array con las variables de chat
|
||||
*/
|
||||
private function getChatVars(array $settings): array
|
||||
{
|
||||
return [
|
||||
'provider' => $settings['chat.provider'] ?? '',
|
||||
'whatsapp_number' => $settings['chat.whatsapp_number'] ?? '',
|
||||
'whatsapp_message' => $settings['chat.whatsapp_message'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene las variables de contacto.
|
||||
*
|
||||
* @return array Array con las variables de contacto
|
||||
*/
|
||||
public function getContactVars(): array
|
||||
{
|
||||
$settings = Setting::withVirtualValue()
|
||||
->where('key', 'LIKE', 'contact.%')
|
||||
->pluck('value', 'key')
|
||||
->toArray();
|
||||
|
||||
return [
|
||||
'phone_number' => isset($settings['contact.phone_number'])
|
||||
? preg_replace('/\D/', '', $settings['contact.phone_number']) // Elimina todo lo que no sea un número
|
||||
: '',
|
||||
'phone_number_text' => $settings['contact.phone_number'] ?? '',
|
||||
'phone_number_ext' => $settings['contact.phone_number_ext'] ?? '',
|
||||
'phone_number_2' => isset($settings['contact.phone_number_2'])
|
||||
? preg_replace('/\D/', '', $settings['contact.phone_number_2']) // Elimina todo lo que no sea un número
|
||||
: '',
|
||||
'phone_number_2_text' => $settings['contact.phone_number_2'] ?? '',
|
||||
'phone_number_2_ext' => $settings['contact.phone_number_2_ext'] ?? '',
|
||||
'email' => $settings['contact.email'] ?? '',
|
||||
'direccion' => $settings['contact.direccion'] ?? '',
|
||||
'horario' => $settings['contact.horario'] ?? '',
|
||||
'location' => [
|
||||
'lat' => $settings['contact.location.lat'] ?? '',
|
||||
'lng' => $settings['contact.location.lng'] ?? '',
|
||||
],
|
||||
'form' => [
|
||||
'to_email' => $settings['contact.form.to_email'] ?? '',
|
||||
'to_email_cc' => $settings['contact.form.to_email_cc'] ?? '',
|
||||
'subject' => $settings['contact.form.subject'] ?? '',
|
||||
'submit_message' => $settings['contact.form.submit_message'] ?? '',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene las variables de redes sociales.
|
||||
*
|
||||
* @return array Array con las variables de redes sociales
|
||||
*/
|
||||
public function getSocialVars(): array
|
||||
{
|
||||
$social = Setting::withVirtualValue()
|
||||
->where('key', 'LIKE', 'social.%')
|
||||
->pluck('value', 'key')
|
||||
->toArray();
|
||||
|
||||
return [
|
||||
'whatsapp' => $social['social.whatsapp'] ?? '',
|
||||
'whatsapp_message' => $social['social.whatsapp_message'] ?? '',
|
||||
'facebook' => $social['social.facebook'] ?? '',
|
||||
'instagram' => $social['social.instagram'] ?? '',
|
||||
'linkedin' => $social['social.linkedin'] ?? '',
|
||||
'tiktok' => $social['social.tiktok'] ?? '',
|
||||
'x_twitter' => $social['social.x_twitter'] ?? '',
|
||||
'google' => $social['social.google'] ?? '',
|
||||
'pinterest' => $social['social.pinterest'] ?? '',
|
||||
'youtube' => $social['social.youtube'] ?? '',
|
||||
'vimeo' => $social['social.vimeo'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Limpia el caché de las variables del website.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clearWebsiteVarsCache()
|
||||
{
|
||||
Cache::forget("website_settings");
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene las variables de legal notice.
|
||||
*
|
||||
* @param string $legalDocument Documento legal a obtener
|
||||
* @return array Array con las variables de legal notice
|
||||
*/
|
||||
public function getLegalVars($legalDocument = false)
|
||||
{
|
||||
$legal = Setting::withVirtualValue()
|
||||
->where('key', 'LIKE', 'legal_notice.%')
|
||||
->pluck('value', 'key')
|
||||
->toArray();
|
||||
|
||||
$legalDocuments = [
|
||||
'legal_notice.terminos_y_condiciones' => [
|
||||
'title' => 'Términos y condiciones',
|
||||
'enabled' => (bool)($legal['legal_notice.terminos_y_condiciones_enabled'] ?? false),
|
||||
'content' => $legal['legal_notice.terminos_y_condiciones_content'] ?? '',
|
||||
],
|
||||
'legal_notice.aviso_de_privacidad' => [
|
||||
'title' => 'Aviso de privacidad',
|
||||
'enabled' => (bool)($legal['legal_notice.aviso_de_privacidad_enabled'] ?? false),
|
||||
'content' => $legal['legal_notice.aviso_de_privacidad_content'] ?? '',
|
||||
],
|
||||
'legal_notice.politica_de_devoluciones' => [
|
||||
'title' => 'Política de devoluciones y reembolsos',
|
||||
'enabled' => (bool)($legal['legal_notice.politica_de_devoluciones_enabled'] ?? false),
|
||||
'content' => $legal['legal_notice.politica_de_devoluciones_content'] ?? '',
|
||||
],
|
||||
'legal_notice.politica_de_envios' => [
|
||||
'title' => 'Política de envíos',
|
||||
'enabled' => (bool)($legal['legal_notice.politica_de_envios_enabled'] ?? false),
|
||||
'content' => $legal['legal_notice.politica_de_envios_content'] ?? '',
|
||||
],
|
||||
'legal_notice.politica_de_cookies' => [
|
||||
'title' => 'Política de cookies',
|
||||
'enabled' => (bool)($legal['legal_notice.politica_de_cookies_enabled'] ?? false),
|
||||
'content' => $legal['legal_notice.politica_de_cookies_content'] ?? '',
|
||||
],
|
||||
'legal_notice.autorizaciones_y_licencias' => [
|
||||
'title' => 'Autorizaciones y licencias',
|
||||
'enabled' => (bool)($legal['legal_notice.autorizaciones_y_licencias_enabled'] ?? false),
|
||||
'content' => $legal['legal_notice.autorizaciones_y_licencias_content'] ?? '',
|
||||
],
|
||||
'legal_notice.informacion_comercial' => [
|
||||
'title' => 'Información comercial',
|
||||
'enabled' => (bool)($legal['legal_notice.informacion_comercial_enabled'] ?? false),
|
||||
'content' => $legal['legal_notice.informacion_comercial_content'] ?? '',
|
||||
],
|
||||
'legal_notice.consentimiento_para_el_login_de_terceros' => [
|
||||
'title' => 'Consentimiento para el login de terceros',
|
||||
'enabled' => (bool)($legal['legal_notice.consentimiento_para_el_login_de_terceros_enabled'] ?? false),
|
||||
'content' => $legal['legal_notice.consentimiento_para_el_login_de_terceros_content'] ?? '',
|
||||
],
|
||||
'legal_notice.leyendas_de_responsabilidad' => [
|
||||
'title' => 'Leyendas de responsabilidad',
|
||||
'enabled' => (bool)($legal['legal_notice.leyendas_de_responsabilidad_enabled'] ?? false),
|
||||
'content' => $legal['legal_notice.leyendas_de_responsabilidad_content'] ?? '',
|
||||
],
|
||||
];
|
||||
|
||||
return $legalDocument
|
||||
? $legalDocuments[$legalDocument]
|
||||
: $legalDocuments;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -11,7 +11,7 @@
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Koneko\\VuexyWebsiteAdmin\\": ""
|
||||
"Koneko\\VuexyWebsiteAdmin\\": "./"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('faq_categories', function (Blueprint $table) {
|
||||
$table->smallIncrements('id');
|
||||
|
||||
$table->string('name')->unique();
|
||||
$table->string('icon')->nullable();
|
||||
$table->unsignedInteger('order')->default(0)->index();
|
||||
$table->boolean('is_active')->default(true)->index();
|
||||
|
||||
// Auditoria
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('faq_categories');
|
||||
}
|
||||
};
|
38
database/migrations/2024_12_29_081815_create_faqs_table.php
Normal file
38
database/migrations/2024_12_29_081815_create_faqs_table.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('faqs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->unsignedSmallInteger('category_id')->nullable()->index();
|
||||
$table->string('question');
|
||||
$table->text('answer');
|
||||
$table->unsignedInteger('order')->default(0)->index();
|
||||
$table->boolean('is_active')->default(true)->index();
|
||||
|
||||
// Auditoria
|
||||
$table->timestamps();
|
||||
|
||||
// Relaciones
|
||||
$table->foreign('category_id')->references('id')->on('faq_categories')->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('faqs');
|
||||
}
|
||||
};
|
23
database/migrations/create_sitemap_configurations_table.php
Normal file
23
database/migrations/create_sitemap_configurations_table.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('sitemap_configurations', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('route');
|
||||
$table->boolean('include')->default(true);
|
||||
$table->decimal('priority', 2, 1)->default(0.5);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sitemap_configurations');
|
||||
}
|
||||
};
|
32
database/migrations/create_sitemap_urls_table.php
Normal file
32
database/migrations/create_sitemap_urls_table.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('sitemap_urls', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('url')->unique();
|
||||
$table->enum('changefreq', ['always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly'])->default('weekly');
|
||||
$table->decimal('priority', 2, 1)->default(0.5);
|
||||
$table->timestamp('lastmod')->nullable();
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sitemap_urls');
|
||||
}
|
||||
};
|
73
resources/js/chat-settings-card.js
Normal file
73
resources/js/chat-settings-card.js
Normal file
@ -0,0 +1,73 @@
|
||||
import '@vuexy-admin/notifications/LivewireNotification.js';
|
||||
import FormCustomListener from '@vuexy-admin/forms/formCustomListener';
|
||||
import registerLivewireHookOnce from '@vuexy-admin/livewire/registerLivewireHookOnce';
|
||||
|
||||
// Inicializar formularios de ajustes de chat
|
||||
window.ChatSettingsForm = new FormCustomListener({
|
||||
formSelector: '#website-chat-settings-card',
|
||||
buttonSelectors: ['.btn-save', '.btn-cancel'],
|
||||
callbacks: [() => {}],
|
||||
dispatchOnSubmit: 'save',
|
||||
validationConfig: {
|
||||
fields: {
|
||||
chat_whatsapp_number: {
|
||||
validators: {
|
||||
callback: {
|
||||
message: 'Por favor, introduce un número de teléfono válido para México.',
|
||||
callback: function (input) {
|
||||
// Obtener el proveedor directamente dentro de la validación
|
||||
const provider = document.querySelector('#chat_provider')?.value;
|
||||
|
||||
// Validar solo si el proveedor es WhatsApp
|
||||
if (provider !== 'whatsapp') return true;
|
||||
|
||||
const cleanValue = input.value.replace(/\D/g, '');
|
||||
const regex = /^[1-9]\d{9}$/; // Exactamente 10 dígitos
|
||||
|
||||
return regex.test(cleanValue);
|
||||
}
|
||||
},
|
||||
notEmpty: {
|
||||
message: 'El número de teléfono es obligatorio.',
|
||||
enabled: () => {
|
||||
// Obtener el proveedor directamente dentro de la validación
|
||||
const provider = document.querySelector('#chat_provider')?.value;
|
||||
|
||||
return provider === 'whatsapp'; // Habilita solo si es WhatsApp
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
chat_whatsapp_message: {
|
||||
validators: {
|
||||
stringLength: {
|
||||
max: 500,
|
||||
message: 'El mensaje no puede exceder los 500 caracteres.'
|
||||
},
|
||||
notEmpty: {
|
||||
message: 'El mensaje es obligatorio.',
|
||||
enabled: () => {
|
||||
// Obtener el proveedor directamente dentro de la validación
|
||||
const provider = document.querySelector('#chat_provider')?.value;
|
||||
|
||||
return provider === 'whatsapp'; // Habilita solo si es WhatsApp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
trigger: new FormValidation.plugins.Trigger(),
|
||||
bootstrap5: new FormValidation.plugins.Bootstrap5({
|
||||
eleValidClass: '',
|
||||
rowSelector: '.fv-row'
|
||||
}),
|
||||
submitButton: new FormValidation.plugins.SubmitButton(),
|
||||
autoFocus: new FormValidation.plugins.AutoFocus()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerLivewireHookOnce('morphed', 'vuexy-website-admin::chat-settings', (component) => {
|
||||
ChatSettingsForm.reloadValidation();
|
||||
});
|
86
resources/js/contact-form-settings-card.js
Normal file
86
resources/js/contact-form-settings-card.js
Normal file
@ -0,0 +1,86 @@
|
||||
import '@vuexy-admin/notifications/LivewireNotification.js';
|
||||
import FormCustomListener from '@vuexy-admin/forms/formCustomListener';
|
||||
import registerLivewireHookOnce from '@vuexy-admin/livewire/registerLivewireHookOnce';
|
||||
|
||||
// Inicializar formularios de ajustes de Formularios de contacto
|
||||
window.ContactFormSettingsForm = new FormCustomListener({
|
||||
formSelector: '#website-contact-form-settings-card',
|
||||
buttonSelectors: ['.btn-save', '.btn-cancel'],
|
||||
callbacks: [() => {}],
|
||||
dispatchOnSubmit: 'save',
|
||||
validationConfig: {
|
||||
fields: {
|
||||
// Validación para correo electrónico de recepción
|
||||
to_email: {
|
||||
validators: {
|
||||
emailAddress: {
|
||||
message: 'Por favor, introduce un correo electrónico válido.'
|
||||
},
|
||||
notEmpty: {
|
||||
message: 'El correo electrónico es obligatorio.'
|
||||
}
|
||||
}
|
||||
},
|
||||
// Validación para correo electrónico con copia
|
||||
to_email_cc: {
|
||||
validators: {
|
||||
emailAddress: {
|
||||
message: 'Por favor, introduce un correo electrónico válido.'
|
||||
},
|
||||
// Validación personalizada para comparar ambos correos electrónicos
|
||||
callback: {
|
||||
message: 'Los correos electrónicos deben ser diferentes.',
|
||||
callback: function (input) {
|
||||
const email = document.querySelector('[name="to_email"]').value.trim();
|
||||
const emailCC = input.value.trim();
|
||||
|
||||
// Si ambos correos son iguales, la validación falla
|
||||
if (email === emailCC) {
|
||||
return false; // Los correos son iguales, por lo que la validación falla
|
||||
}
|
||||
|
||||
return true; // Si son diferentes, la validación pasa
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// Validación para el asunto del formulario de contacto
|
||||
subject: {
|
||||
validators: {
|
||||
stringLength: {
|
||||
max: 60,
|
||||
message: 'El título del correo no puede exceder los 60 caracteres.'
|
||||
},
|
||||
notEmpty: {
|
||||
message: 'El título del correo es obligatorio.'
|
||||
}
|
||||
}
|
||||
},
|
||||
// Validación para el mensaje de envío
|
||||
submit_message: {
|
||||
validators: {
|
||||
stringLength: {
|
||||
max: 250,
|
||||
message: 'El mensaje no puede exceder los 250 caracteres.'
|
||||
},
|
||||
notEmpty: {
|
||||
message: 'El mensaje de envío es obligatorio.'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
trigger: new FormValidation.plugins.Trigger(),
|
||||
bootstrap5: new FormValidation.plugins.Bootstrap5({
|
||||
eleValidClass: '',
|
||||
rowSelector: '.fv-row'
|
||||
}),
|
||||
submitButton: new FormValidation.plugins.SubmitButton(),
|
||||
autoFocus: new FormValidation.plugins.AutoFocus()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerLivewireHookOnce('morphed', 'vuexy-website-admin::contact-form-settings', (component) => {
|
||||
ContactFormSettingsForm.reloadValidation();
|
||||
});
|
213
resources/js/contact-info-settings-card.js
Normal file
213
resources/js/contact-info-settings-card.js
Normal file
@ -0,0 +1,213 @@
|
||||
import '@vuexy-admin/notifications/LivewireNotification.js';
|
||||
import FormCustomListener from '@vuexy-admin/forms/formCustomListener';
|
||||
import registerLivewireHookOnce from '@vuexy-admin/livewire/registerLivewireHookOnce';
|
||||
|
||||
// Inicializar formularios de ajustes de información de contacto
|
||||
window.ContactInfoSettingsForm = new FormCustomListener({
|
||||
formSelector: '#website-contact-info-settings-card',
|
||||
buttonSelectors: ['.btn-save', '.btn-cancel'],
|
||||
callbacks: [() => {}],
|
||||
dispatchOnSubmit: 'save',
|
||||
validationConfig: {
|
||||
fields: {
|
||||
// Validación para número telefónico
|
||||
phone_number: {
|
||||
validators: {
|
||||
callback: {
|
||||
message: 'Por favor, introduce un número de teléfono válido para México.',
|
||||
callback: function (input) {
|
||||
// Si el campo está vacío, no hacemos validación
|
||||
if (input.value.trim() === '') {
|
||||
return true; // Permitir vacío
|
||||
}
|
||||
|
||||
// Si no está vacío, validamos el formato del número
|
||||
const cleanValue = input.value.replace(/\D/g, '');
|
||||
const regex = /^[1-9]\d{9}$/; // Exactamente 10 dígitos
|
||||
|
||||
return regex.test(cleanValue); // Valida solo si hay un número
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// Validación para extensión telefónica (opcional, pero solo si phone_number tiene valor)
|
||||
phone_number_ext: {
|
||||
validators: {
|
||||
stringLength: {
|
||||
max: 10,
|
||||
message: 'La extensión no debe exceder los 10 caracteres.'
|
||||
},
|
||||
callback: {
|
||||
message: 'La extensión requiere de ingresar un número telefónico.',
|
||||
callback: function (input) {
|
||||
// Obtener el valor de 'phone_number'
|
||||
const phoneNumber = document.querySelector('[name="phone_number"]')?.value.trim();
|
||||
|
||||
// Si el número telefónico tiene valor, entonces la extensión es obligatoria
|
||||
if (phoneNumber !== '') {
|
||||
// Si la extensión está vacía, la validación falla
|
||||
return true; // Permitir vacío
|
||||
}
|
||||
|
||||
// Si no se ha ingresado un número telefónico, la extensión no debe tener valor
|
||||
return input.value.trim() === '';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// Validación para número telefónico
|
||||
phone_number_2: {
|
||||
validators: {
|
||||
callback: {
|
||||
message: 'Por favor, introduce un número de teléfono válido para México.',
|
||||
callback: function (input) {
|
||||
// Si el campo está vacío, no hacemos validación
|
||||
if (input.value.trim() === '') {
|
||||
return true; // Permitir vacío
|
||||
}
|
||||
|
||||
// Si no está vacío, validamos el formato del número
|
||||
const cleanValue = input.value.replace(/\D/g, '');
|
||||
const regex = /^[1-9]\d{9}$/; // Exactamente 10 dígitos
|
||||
|
||||
return regex.test(cleanValue); // Valida solo si hay un número
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// Validación para extensión telefónica (opcional, pero solo si phone_number tiene valor)
|
||||
phone_number_2_ext: {
|
||||
validators: {
|
||||
stringLength: {
|
||||
max: 10,
|
||||
message: 'La extensión no debe exceder los 10 caracteres.'
|
||||
},
|
||||
callback: {
|
||||
message: 'La extensión requiere de ingresar un número telefónico.',
|
||||
callback: function (input) {
|
||||
// Obtener el valor de 'phone_number'
|
||||
const phoneNumber = document.querySelector('[name="phone_number_2"]')?.value.trim();
|
||||
|
||||
// Si el número telefónico tiene valor, entonces la extensión es obligatoria
|
||||
if (phoneNumber !== '') {
|
||||
// Si la extensión está vacía, la validación falla
|
||||
return true; // Permitir vacío
|
||||
}
|
||||
|
||||
// Si no se ha ingresado un número telefónico, la extensión no debe tener valor
|
||||
return input.value.trim() === '';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// Validación para correo electrónico de contacto (opcional)
|
||||
email: {
|
||||
validators: {
|
||||
emailAddress: {
|
||||
message: 'Por favor, introduce un correo electrónico válido.'
|
||||
}
|
||||
}
|
||||
},
|
||||
// Validación para horario (No obligatorio, máximo 160 caracteres)
|
||||
horario: {
|
||||
validators: {
|
||||
stringLength: {
|
||||
max: 160,
|
||||
message: 'El horario no puede exceder los 160 caracteres.'
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
trigger: new FormValidation.plugins.Trigger(),
|
||||
bootstrap5: new FormValidation.plugins.Bootstrap5({
|
||||
eleValidClass: '',
|
||||
rowSelector: '.fv-row'
|
||||
}),
|
||||
submitButton: new FormValidation.plugins.SubmitButton(),
|
||||
autoFocus: new FormValidation.plugins.AutoFocus()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerLivewireHookOnce('morphed', 'vuexy-website-admin::contact-info-settings', (component) => {
|
||||
ContactInfoSettingsForm.reloadValidation();
|
||||
});
|
||||
|
||||
// Inicializar formularios de ajustes de ubicación
|
||||
window.LocationSettingsForm = new FormCustomListener({
|
||||
formSelector: '#website-location-settings-card',
|
||||
buttonSelectors: ['.btn-save', '.btn-cancel'],
|
||||
callbacks: [() => {}],
|
||||
dispatchOnSubmit: 'save',
|
||||
validationConfig: {
|
||||
fields: {
|
||||
// Validación para dirección (No obligatorio, máximo 160 caracteres)
|
||||
direccion: {
|
||||
validators: {
|
||||
stringLength: {
|
||||
max: 160,
|
||||
message: 'La dirección no puede exceder los 160 caracteres.'
|
||||
}
|
||||
}
|
||||
},
|
||||
// Validación para latitud (No obligatorio, pero debe ser un número si se ingresa)
|
||||
location_lat: {
|
||||
validators: {
|
||||
numeric: {
|
||||
message: 'La latitud debe ser un número.'
|
||||
},
|
||||
callback: {
|
||||
message: 'La latitud es obligatoria si se ingresa longitud.',
|
||||
callback: function (input) {
|
||||
// Obtener el valor de longitud
|
||||
const longitude = document.querySelector('[name="location_lng"]')?.value.trim();
|
||||
|
||||
// Si longitud tiene un valor, entonces latitud es obligatorio
|
||||
if (longitude !== '') {
|
||||
return input.value.trim() !== ''; // La latitud no puede estar vacía
|
||||
}
|
||||
|
||||
return true; // Si longitud está vacío, no se valida latitud
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// Validación para longitud (No obligatorio, pero debe ser un número si se ingresa)
|
||||
location_lng: {
|
||||
validators: {
|
||||
numeric: {
|
||||
message: 'La longitud debe ser un número.'
|
||||
},
|
||||
callback: {
|
||||
message: 'La longitud es obligatoria si se ingresa latitud.',
|
||||
callback: function (input) {
|
||||
// Obtener el valor de latitud
|
||||
const latitude = document.querySelector('[name="location_lat"]')?.value.trim();
|
||||
|
||||
// Si latitud tiene un valor, entonces longitud es obligatorio
|
||||
if (latitude !== '') {
|
||||
return input.value.trim() !== ''; // La longitud no puede estar vacía
|
||||
}
|
||||
|
||||
return true; // Si latitud está vacío, no se valida longitud
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
trigger: new FormValidation.plugins.Trigger(),
|
||||
bootstrap5: new FormValidation.plugins.Bootstrap5({
|
||||
eleValidClass: '',
|
||||
rowSelector: '.fv-row'
|
||||
}),
|
||||
submitButton: new FormValidation.plugins.SubmitButton(),
|
||||
autoFocus: new FormValidation.plugins.AutoFocus()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerLivewireHookOnce('morphed', 'vuexy-website-admin::location-settings', (component) => {
|
||||
LocationSettingsForm.reloadValidation();
|
||||
});
|
41
resources/js/google-analytics-settings-card.js
Normal file
41
resources/js/google-analytics-settings-card.js
Normal file
@ -0,0 +1,41 @@
|
||||
import '@vuexy-admin/notifications/LivewireNotification.js';
|
||||
import FormCustomListener from '@vuexy-admin/forms/formCustomListener';
|
||||
import registerLivewireHookOnce from '@vuexy-admin/livewire/registerLivewireHookOnce';
|
||||
|
||||
// Inicializar formularios de ajustes de análisis de datos
|
||||
window.AnalyticsSettingsForm = new FormCustomListener({
|
||||
formSelector: '#website-analytics-settings-card',
|
||||
buttonSelectors: ['.btn-save', '.btn-cancel'],
|
||||
callbacks: [() => {}],
|
||||
dispatchOnSubmit: 'save',
|
||||
validationConfig: {
|
||||
fields: {
|
||||
google_analytics_id: {
|
||||
validators: {
|
||||
callback: {
|
||||
message: 'ID de medición de Google Analytics no tienen un formato válido.',
|
||||
callback: function (input) {
|
||||
if (document.getElementById('google_analytics_enabled').checked) {
|
||||
return input.value.trim() !== '';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
trigger: new FormValidation.plugins.Trigger(),
|
||||
bootstrap5: new FormValidation.plugins.Bootstrap5({
|
||||
eleValidClass: '',
|
||||
rowSelector: '.fv-row'
|
||||
}),
|
||||
submitButton: new FormValidation.plugins.SubmitButton(),
|
||||
autoFocus: new FormValidation.plugins.AutoFocus()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerLivewireHookOnce('morphed', 'vuexy-website-admin::analytics-index', (component) => {
|
||||
AnalyticsSettingsForm.reloadValidation();
|
||||
});
|
133
resources/js/website-settings-card.js
Normal file
133
resources/js/website-settings-card.js
Normal file
@ -0,0 +1,133 @@
|
||||
import '@vuexy-admin/notifications/LivewireNotification.js';
|
||||
import FormCustomListener from '@vuexy-admin/forms/formCustomListener';
|
||||
import registerLivewireHookOnce from '@vuexy-admin/livewire/registerLivewireHookOnce';
|
||||
|
||||
// Inicializar formularios de ajustes de social media
|
||||
window.SocialSettingsForm = new FormCustomListener({
|
||||
formSelector: '#website-social-settings-card',
|
||||
buttonSelectors: ['.btn-save', '.btn-cancel'],
|
||||
callbacks: [() => {}],
|
||||
dispatchOnSubmit: 'save',
|
||||
validationConfig: {
|
||||
fields: {
|
||||
social_whatsapp: {
|
||||
validators: {
|
||||
callback: {
|
||||
message: 'Por favor, introduce un número de teléfono válido para México.',
|
||||
callback: function (input) {
|
||||
// Si el campo está vacío, no hacemos validación
|
||||
if (input.value.trim() === '') {
|
||||
return true; // Permitir vacío
|
||||
}
|
||||
|
||||
// Si no está vacío, validamos el formato del número
|
||||
const cleanValue = input.value.replace(/\D/g, '');
|
||||
const regex = /^[1-9]\d{9}$/; // Exactamente 10 dígitos
|
||||
|
||||
return regex.test(cleanValue); // Valida solo si hay un número
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
social_whatsapp_message: {
|
||||
validators: {
|
||||
stringLength: {
|
||||
max: 500,
|
||||
message: 'El mensaje no puede exceder los 500 caracteres.'
|
||||
},
|
||||
callback: {
|
||||
message: 'El mensaje es obligatorio.',
|
||||
callback: function (input) {
|
||||
// Obtener el valor de 'social_whatsapp'
|
||||
const whatsappNumber = document.querySelector('#social_whatsapp').value.trim();
|
||||
|
||||
// Si 'social_whatsapp' tiene un valor, entonces el mensaje es obligatorio
|
||||
if (whatsappNumber !== '') {
|
||||
return input.value.trim() !== ''; // El mensaje no puede estar vacío
|
||||
}
|
||||
|
||||
return true; // Si 'social_whatsapp' está vacío, no validamos 'social_whatsapp_message'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
social_facebook: {
|
||||
validators: {
|
||||
uri: {
|
||||
message: 'Por favor, introduce una URL válida.'
|
||||
}
|
||||
}
|
||||
},
|
||||
social_instagram: {
|
||||
validators: {
|
||||
uri: {
|
||||
message: 'Por favor, introduce una URL válida.'
|
||||
}
|
||||
}
|
||||
},
|
||||
social_linkedin: {
|
||||
validators: {
|
||||
uri: {
|
||||
message: 'Por favor, introduce una URL válida.'
|
||||
}
|
||||
}
|
||||
},
|
||||
social_tiktok: {
|
||||
validators: {
|
||||
uri: {
|
||||
message: 'Por favor, introduce una URL válida.'
|
||||
}
|
||||
}
|
||||
},
|
||||
social_x_twitter: {
|
||||
validators: {
|
||||
uri: {
|
||||
message: 'Por favor, introduce una URL válida.'
|
||||
}
|
||||
}
|
||||
},
|
||||
social_google: {
|
||||
validators: {
|
||||
uri: {
|
||||
message: 'Por favor, introduce una URL válida.'
|
||||
}
|
||||
}
|
||||
},
|
||||
social_pinterest: {
|
||||
validators: {
|
||||
uri: {
|
||||
message: 'Por favor, introduce una URL válida.'
|
||||
}
|
||||
}
|
||||
},
|
||||
social_youtube: {
|
||||
validators: {
|
||||
uri: {
|
||||
message: 'Por favor, introduce una URL válida.'
|
||||
}
|
||||
}
|
||||
},
|
||||
social_vimeo: {
|
||||
validators: {
|
||||
uri: {
|
||||
message: 'Por favor, introduce una URL válida.'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
trigger: new FormValidation.plugins.Trigger(),
|
||||
bootstrap5: new FormValidation.plugins.Bootstrap5({
|
||||
eleValidClass: '',
|
||||
rowSelector: '.fv-row',
|
||||
messageContainer: '.fv-message'
|
||||
}),
|
||||
submitButton: new FormValidation.plugins.SubmitButton(),
|
||||
autoFocus: new FormValidation.plugins.AutoFocus()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerLivewireHookOnce('morphed', 'vuexy-website-admin::social-media-settings', (component) => {
|
||||
SocialSettingsForm.reloadValidation();
|
||||
});
|
29
resources/views/chat/index.blade.php
Normal file
29
resources/views/chat/index.blade.php
Normal file
@ -0,0 +1,29 @@
|
||||
@extends('vuexy-admin::layouts.vuexy.layoutMaster')
|
||||
|
||||
@section('title', 'Chat')
|
||||
|
||||
@section('vendor-style')
|
||||
@vite([
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/form-validation.scss'
|
||||
])
|
||||
@endsection
|
||||
|
||||
@section('vendor-script')
|
||||
@vite([
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/popular.js',
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/bootstrap5.js',
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/auto-focus.js',
|
||||
])
|
||||
@endsection
|
||||
|
||||
@push('page-script')
|
||||
@vite('vendor/koneko/laravel-vuexy-website-admin/resources/js/chat-settings-card.js')
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@livewire('vuexy-website-admin::chat-settings')
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
29
resources/views/contact-form/index.blade.php
Normal file
29
resources/views/contact-form/index.blade.php
Normal file
@ -0,0 +1,29 @@
|
||||
@extends('vuexy-admin::layouts.vuexy.layoutMaster')
|
||||
|
||||
@section('title', 'Formulario de Contacto')
|
||||
|
||||
@section('vendor-style')
|
||||
@vite([
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/form-validation.scss'
|
||||
])
|
||||
@endsection
|
||||
|
||||
@section('vendor-script')
|
||||
@vite([
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/popular.js',
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/bootstrap5.js',
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/auto-focus.js',
|
||||
])
|
||||
@endsection
|
||||
|
||||
@push('page-script')
|
||||
@vite('vendor/koneko/laravel-vuexy-website-admin/resources/js/contact-form-settings-card.js')
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@livewire('vuexy-website-admin::contact-form-settings')
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
32
resources/views/contact-info/index.blade.php
Normal file
32
resources/views/contact-info/index.blade.php
Normal file
@ -0,0 +1,32 @@
|
||||
@extends('vuexy-admin::layouts.vuexy.layoutMaster')
|
||||
|
||||
@section('title', 'Información de Contacto')
|
||||
|
||||
@section('vendor-style')
|
||||
@vite([
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/form-validation.scss'
|
||||
])
|
||||
@endsection
|
||||
|
||||
@section('vendor-script')
|
||||
@vite([
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/popular.js',
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/bootstrap5.js',
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/auto-focus.js',
|
||||
])
|
||||
@endsection
|
||||
|
||||
@push('page-script')
|
||||
@vite('vendor/koneko/laravel-vuexy-website-admin/resources/js/contact-info-settings-card.js')
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@livewire('vuexy-website-admin::contact-info-settings')
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
@livewire('vuexy-website-admin::location-settings')
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
23
resources/views/faq/index.blade.php
Normal file
23
resources/views/faq/index.blade.php
Normal file
@ -0,0 +1,23 @@
|
||||
@extends('vuexy-admin::layouts.vuexy.layoutMaster')
|
||||
|
||||
@section('title', 'Preguntas Frecuentes')
|
||||
|
||||
@section('vendor-style')
|
||||
@vite([
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/select2/select2.scss',
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/bootstrap-table/bootstrap-table.scss',
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/fonts/bootstrap-icons.scss',
|
||||
])
|
||||
@endsection
|
||||
|
||||
@push('page-script')
|
||||
@vite([
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/select2/select2.js',
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/js/bootstrap-table/bootstrapTableManager.js',
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/js/forms/formConvasHelper.js',
|
||||
])
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
@livewire('vuexy-website-admin::faq-index')
|
||||
@endsection
|
20
resources/views/general-settings/index.blade.php
Normal file
20
resources/views/general-settings/index.blade.php
Normal file
@ -0,0 +1,20 @@
|
||||
@extends('vuexy-admin::layouts.vuexy.layoutMaster')
|
||||
|
||||
@section('title', 'Ajustes Generales')
|
||||
|
||||
@push('page-script')
|
||||
@vite('vendor/koneko/laravel-vuexy-admin/resources/js/pages/admin-settings-scripts.js')
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
@livewire('vuexy-website-admin::website-description-settings')
|
||||
@livewire('vuexy-website-admin::website-favicon-settings')
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
@livewire('vuexy-website-admin::logo-on-light-bg-settings')
|
||||
@livewire('vuexy-website-admin::logo-on-dark-bg-settings')
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
29
resources/views/google-analytics/index.blade.php
Normal file
29
resources/views/google-analytics/index.blade.php
Normal file
@ -0,0 +1,29 @@
|
||||
@extends('vuexy-admin::layouts.vuexy.layoutMaster')
|
||||
|
||||
@section('title', 'Google Analytics')
|
||||
|
||||
@section('vendor-style')
|
||||
@vite([
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/form-validation.scss'
|
||||
])
|
||||
@endsection
|
||||
|
||||
@section('vendor-script')
|
||||
@vite([
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/popular.js',
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/bootstrap5.js',
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/auto-focus.js',
|
||||
])
|
||||
@endsection
|
||||
|
||||
@push('page-script')
|
||||
@vite('vendor/koneko/laravel-vuexy-website-admin/resources/js/google-analytics-settings-card.js')
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@livewire('vuexy-website-admin::google-analytics-settings')
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
11
resources/views/images/index.blade.php
Normal file
11
resources/views/images/index.blade.php
Normal file
@ -0,0 +1,11 @@
|
||||
@extends('vuexy-admin::layouts.vuexy.layoutMaster')
|
||||
|
||||
@section('title', 'Galería de Imágenes')
|
||||
|
||||
@push('page-script')
|
||||
@vite('vendor/koneko/laravel-vuexy-admin/resources/js/pages/admin-settings-scripts.js')
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
@livewire('vuexy-website-admin::images-index')
|
||||
@endsection
|
7
resources/views/legal-notices/index.blade.php
Normal file
7
resources/views/legal-notices/index.blade.php
Normal file
@ -0,0 +1,7 @@
|
||||
@extends('vuexy-admin::layouts.vuexy.layoutMaster')
|
||||
|
||||
@section('title', 'Avisos Legales')
|
||||
|
||||
@section('content')
|
||||
@livewire('vuexy-website-admin::legal-notices-index')
|
||||
@endsection
|
28
resources/views/legal-notices/legal-index.blade.php
Normal file
28
resources/views/legal-notices/legal-index.blade.php
Normal file
@ -0,0 +1,28 @@
|
||||
@extends('admin::layouts.vuexy.layoutMaster')
|
||||
|
||||
@section('title', 'Avisos legales')
|
||||
|
||||
<!-- Vendor Styles -->
|
||||
@section('vendor-style')
|
||||
@vite([
|
||||
'modules/Admin/Resources/assets/vendor/libs/quill/typography.scss',
|
||||
//'modules/Admin/Resources/assets/vendor/libs/quill/katex.scss',
|
||||
'modules/Admin/Resources/assets/vendor/libs/quill/editor.scss'
|
||||
])
|
||||
@endsection
|
||||
|
||||
<!-- Vendor Scripts -->
|
||||
@section('vendor-script')
|
||||
@vite([
|
||||
//'modules/Admin/Resources/assets/vendor/libs/quill/katex.js',
|
||||
'modules/Admin/Resources/assets/vendor/libs/quill/quill.js'
|
||||
])
|
||||
@endsection
|
||||
|
||||
@section('page-script')
|
||||
@vite('modules/Admin/Resources/js/website-settings/legal-settings-scripts.js')
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
@livewire('website-legal-settings')
|
||||
@endsection
|
7
resources/views/livewire/faq/index.blade.php
Normal file
7
resources/views/livewire/faq/index.blade.php
Normal file
@ -0,0 +1,7 @@
|
||||
<x-vuexy-admin::table.bootstrap.manager :tagName="$tagName" :datatableConfig="$bt_datatable">
|
||||
<x-slot name="tools">
|
||||
<div class="mb-4 pr-2">
|
||||
<x-vuexy-admin::button.index-offcanvas :label="$singularName" :tagName="$tagName" />
|
||||
</div>
|
||||
</x-slot>
|
||||
</x-vuexy-admin::table.bootstrap.manager>
|
12
resources/views/livewire/images/index.blade.php
Normal file
12
resources/views/livewire/images/index.blade.php
Normal file
@ -0,0 +1,12 @@
|
||||
<form id="css-form">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<h5 class="card-header">Imagenes</h5>
|
||||
<div class="card-body">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
70
resources/views/livewire/legal-notices/index.blade.php
Normal file
70
resources/views/livewire/legal-notices/index.blade.php
Normal file
@ -0,0 +1,70 @@
|
||||
<div>
|
||||
<x-vuexy-admin::form.form id="website-legal-notices-settings-card" class="form-custom-listener mb-4" wire:ignore.self>
|
||||
<x-vuexy-admin::card.basic title="Avisos Legales" class="mb-2">
|
||||
{{-- Selector de sección --}}
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
@foreach($legalVars as $key => $section)
|
||||
<li class="nav-item">
|
||||
<button
|
||||
type="button"
|
||||
class="nav-link @if($currentSection === $key) active @endif"
|
||||
onclick="@this.currentSection = '{{ $key }}';"
|
||||
role="tab"
|
||||
data-bs-toggle="tab"
|
||||
data-bs-target="#{{ $key }}-nav"
|
||||
aria-controls="{{ $key }}-nav"
|
||||
aria-selected="@if($currentSection === $key) true @else false @endif">
|
||||
{{ $section['title'] }}
|
||||
</button>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
@foreach($legalVars as $key => $section)
|
||||
<div class="tab-pane fade @if($currentSection === $key) show active @endif" id="{{ $key }}-nav" role="tabpanel">
|
||||
{{-- Habilitar sección --}}
|
||||
<x-vuexy-admin::form.checkbox
|
||||
model="legalVars.{{ $key }}.enabled"
|
||||
label="Habilitar sección"
|
||||
switch />
|
||||
|
||||
{{-- Editor de contenido --}}
|
||||
<x-vuexy-admin::form.textarea
|
||||
model="legalVars.{{ $key }}.content"
|
||||
label="Contenido"
|
||||
switch
|
||||
required />
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-vuexy-admin::card.basic>
|
||||
|
||||
{{-- Botones de acción --}}
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-end">
|
||||
<x-vuexy-admin::button.basic
|
||||
type="submit"
|
||||
variant="primary"
|
||||
size="sm"
|
||||
icon="ti ti-device-floppy"
|
||||
label="Guardar cambios"
|
||||
disabled
|
||||
class="btn-save mt-2 mr-2"
|
||||
waves
|
||||
data-loading-text="Guardando..." />
|
||||
|
||||
<x-vuexy-admin::button.basic
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon="ti ti-rotate-2"
|
||||
label="Cancelar"
|
||||
wire:click="loadSettings"
|
||||
class="btn-cancel mt-2 mr-2"
|
||||
waves />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Contenedor para notificaciones --}}
|
||||
<div class="notification-container pt-4" wire:ignore></div>
|
||||
</x-vuexy-admin::form.form>
|
||||
</div>
|
22
resources/views/livewire/sitemap-manager/index.blade.php
Normal file
22
resources/views/livewire/sitemap-manager/index.blade.php
Normal file
@ -0,0 +1,22 @@
|
||||
<div>
|
||||
<h2>Gestión del Sitemap</h2>
|
||||
|
||||
<input type="text" wire:model="newUrl" placeholder="Nueva URL">
|
||||
<select wire:model="changefreq">
|
||||
<option value="daily">Diario</option>
|
||||
<option value="weekly">Semanal</option>
|
||||
<option value="monthly">Mensual</option>
|
||||
</select>
|
||||
<input type="number" step="0.1" wire:model="priority" min="0.1" max="1.0">
|
||||
<button wire:click="addUrl">Agregar</button>
|
||||
|
||||
<ul>
|
||||
@foreach($urls as $url)
|
||||
<li>{{ $url->url }} ({{ $url->changefreq }}, {{ $url->priority }})
|
||||
<button wire:click="deleteUrl({{ $url->id }})">❌</button>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
<button wire:click="$emit('generateSitemap')">Regenerar Sitemap</button>
|
||||
</div>
|
34
resources/views/livewire/vuexy/analytics-settings.blade.php
Normal file
34
resources/views/livewire/vuexy/analytics-settings.blade.php
Normal file
@ -0,0 +1,34 @@
|
||||
<div x-data="{ googleanalyticsEnabled: @entangle('google_analytics_enabled') }">
|
||||
<x-vuexy-admin::form.form id="website-analytics-settings-card" class="form-custom-listener mb-4" whitOutId whitOutMode>
|
||||
<x-vuexy-admin::card.basic title="Google Analytics" class="mb-2">
|
||||
<div class="mb-6">
|
||||
<a href="https://analytics.google.com/analytics/web/">https://analytics.google.com/analytics/web/</a>
|
||||
</div>
|
||||
<x-vuexy-admin::form.checkbox model="google_analytics_enabled" label="Habilitar Google Analytics" switch />
|
||||
<x-vuexy-admin::form.input model="google_analytics_id" label="ID de medición de Google Analytics" icon="fab fa-google" placeholder="XX-12345678901" x-bind:disabled='!googleanalyticsEnabled' />
|
||||
</x-vuexy-admin::card.basic>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-end">
|
||||
<x-vuexy-admin::button.basic
|
||||
type="submit"
|
||||
variant="primary"
|
||||
size="sm"
|
||||
icon="ti ti-device-floppy"
|
||||
label="Guardar cambios"
|
||||
disabled
|
||||
class="btn-save mt-2 mr-2 waves-effect waves-light"
|
||||
waves
|
||||
data-loading-text="Guardando..." />
|
||||
<x-vuexy-admin::button.basic
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon="ti ti-rotate-2"
|
||||
label="Cancelar"
|
||||
wire:click="resetForm"
|
||||
class="btn-cancel mt-2 mr-2 waves-effect waves-light"
|
||||
waves />
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification-container pt-4" wire:ignore></div>
|
||||
</x-vuexy-admin::form.form>
|
||||
</div>
|
58
resources/views/livewire/vuexy/chat-settings.blade.php
Normal file
58
resources/views/livewire/vuexy/chat-settings.blade.php
Normal file
@ -0,0 +1,58 @@
|
||||
<div x-data="{ chatProvider: @entangle('chat_provider') }">
|
||||
<x-vuexy-admin::form.form id="website-chat-settings-card" class="form-custom-listener mb-4" whitOutId whitOutMode>
|
||||
<x-vuexy-admin::card.basic title="Configuración del Chat" class="mb-2">
|
||||
{{-- Proveedor --}}
|
||||
<div class="mb-4 fv-row">
|
||||
<label for="chat_provider" class="form-label">Proveedor</label>
|
||||
<select id="chat_provider" name="chat_provider" x-model="chatProvider" wire:model="chat_provider" class="form-select">
|
||||
<option value="">Deshabilitar Chat</option>
|
||||
<option value="whatsapp">WhatsApp</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- Configuración de WhatsApp --}}
|
||||
<div x-show="chatProvider === 'whatsapp'" class="mt-5">
|
||||
<h5>WhatsApp</h5>
|
||||
<x-vuexy-admin::form.input
|
||||
model="chat_whatsapp_number"
|
||||
label="Número telefónico"
|
||||
placeholder="Número telefónico"
|
||||
required />
|
||||
|
||||
<x-vuexy-admin::form.input
|
||||
model="chat_whatsapp_message"
|
||||
label="Mensaje de saludo"
|
||||
placeholder="Mensaje de saludo"
|
||||
required />
|
||||
</div>
|
||||
</x-vuexy-admin::card.basic>
|
||||
|
||||
{{-- Botones de acción --}}
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-end">
|
||||
<x-vuexy-admin::button.basic
|
||||
type="submit"
|
||||
variant="primary"
|
||||
size="sm"
|
||||
icon="ti ti-device-floppy"
|
||||
label="Guardar cambios"
|
||||
disabled
|
||||
class="btn-save mt-2 mr-2"
|
||||
waves
|
||||
data-loading-text="Guardando..." />
|
||||
|
||||
<x-vuexy-admin::button.basic
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon="ti ti-rotate-2"
|
||||
label="Cancelar"
|
||||
wire:click="resetForm"
|
||||
class="btn-cancel mt-2 mr-2"
|
||||
waves />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Contenedor para notificaciones --}}
|
||||
<div class="notification-container pt-4" wire:ignore></div>
|
||||
</x-vuexy-admin::form.form>
|
||||
</div>
|
@ -0,0 +1,33 @@
|
||||
<div>
|
||||
<x-vuexy-admin::form.form id="website-contact-form-settings-card" class="form-custom-listener mb-4" whitOutId whitOutMode>
|
||||
<x-vuexy-admin::card.basic title="Configuración del Formulario" class="mb-2">
|
||||
<x-vuexy-admin::form.input model="to_email" label="Correo principal" type="email" icon="ti ti-mail" placeholder="Email donde se enviarán los mensajes" required />
|
||||
<x-vuexy-admin::form.input model="to_email_cc" label="Correo CC" type="email" icon="ti ti-mail-forward" placeholder="Email adicional para copia" helperText="Email adicional que recibirá una copia de los mensajes" />
|
||||
<x-vuexy-admin::form.input model="subject" label="Asunto del correo" placeholder="Asunto predeterminado del email" required />
|
||||
<x-vuexy-admin::form.textarea model="submit_message" label="Mensaje de Confirmación" placeholder="Mensaje que se mostrará al usuario cuando envíe el formulario" required />
|
||||
</x-vuexy-admin::card.basic>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-end">
|
||||
<x-vuexy-admin::button.basic
|
||||
type="submit"
|
||||
variant="primary"
|
||||
size="sm"
|
||||
icon="ti ti-device-floppy"
|
||||
label="Guardar cambios"
|
||||
disabled
|
||||
class="btn-save mt-2 mr-2"
|
||||
waves
|
||||
data-loading-text="Guardando..." />
|
||||
<x-vuexy-admin::button.basic
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon="ti ti-rotate-2"
|
||||
label="Cancelar"
|
||||
wire:click="resetForm"
|
||||
class="btn-cancel mt-2 mr-2"
|
||||
waves />
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification-container pt-4" wire:ignore></div>
|
||||
</x-vuexy-admin::form.form>
|
||||
</div>
|
@ -0,0 +1,41 @@
|
||||
<div>
|
||||
<x-vuexy-admin::form.form id="website-contact-info-settings-card" class="form-custom-listener mb-4" whitOutId whitOutMode>
|
||||
<x-vuexy-admin::card.basic title="Información de contacto" class="mb-2">
|
||||
<div class="row">
|
||||
<x-vuexy-admin::form.input model="phone_number" label="Número telefónico" icon="ti ti-phone" placeholder="Número telefónico" parentClass="col-md-8" />
|
||||
<x-vuexy-admin::form.input model="phone_number_ext" label="Extención telefónica" icon="ti ti-phone-plus" placeholder="Ext. núm." parentClass="col-md-4" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<x-vuexy-admin::form.input model="phone_number_2" label="Número telefónico alternativo" icon="ti ti-phone" placeholder="Número telefónico alternativo" parentClass="col-md-8" />
|
||||
<x-vuexy-admin::form.input model="phone_number_2_ext" label="Extención telefónica²" icon="ti ti-phone-plus" placeholder="Ext. núm.²" parentClass="col-md-4" />
|
||||
</div>
|
||||
<x-vuexy-admin::form.input model="email" label="Correo electrónico" icon="ti ti-mail" type="email" placeholder="Correo electrónico" />
|
||||
<x-vuexy-admin::form.input model="horario" label="Horario" icon="ti ti-clock" placeholder="Horario" />
|
||||
</x-vuexy-admin::card.basic>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-end">
|
||||
<x-vuexy-admin::button.basic
|
||||
type="submit"
|
||||
variant="primary"
|
||||
size="sm"
|
||||
icon="ti ti-device-floppy"
|
||||
label="Guardar cambios"
|
||||
disabled
|
||||
class="btn-save mt-2 mr-2"
|
||||
waves
|
||||
data-loading-text="Guardando..." />
|
||||
|
||||
<x-vuexy-admin::button.basic
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon="ti ti-rotate-2"
|
||||
label="Cancelar"
|
||||
wire:click="resetForm"
|
||||
class="btn-cancel mt-2 mr-2"
|
||||
waves />
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification-container pt-4" wire:ignore></div>
|
||||
</x-vuexy-admin::form.form>
|
||||
</div>
|
34
resources/views/livewire/vuexy/location-settings.blade.php
Normal file
34
resources/views/livewire/vuexy/location-settings.blade.php
Normal file
@ -0,0 +1,34 @@
|
||||
<div>
|
||||
<x-vuexy-admin::form.form id="website-location-settings-card" class="form-custom-listener mb-4" whitOutId whitOutMode>
|
||||
<x-vuexy-admin::card.basic title="Ubicación y Horarios" class="mb-2">
|
||||
<x-vuexy-admin::form.input model="direccion" label="Dirección" icon="ti ti-map-pin" placeholder="Dirección" />
|
||||
<div class="row">
|
||||
<x-vuexy-admin::form.input type="number" step="0.00000" model="location_lat" label="Latitud" icon="ti ti-map-pin-2" placeholder="Latitud" parentClass="col-6" align="center" />
|
||||
<x-vuexy-admin::form.input type="number" step="0.00000" model="location_lng" label="Longitud" icon="ti ti-map-pin-2" placeholder="Longitud" parentClass="col-6" align="center" />
|
||||
</div>
|
||||
</x-vuexy-admin::card.basic>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-end">
|
||||
<x-vuexy-admin::button.basic
|
||||
type="submit"
|
||||
variant="primary"
|
||||
size="sm"
|
||||
icon="ti ti-device-floppy"
|
||||
label="Guardar cambios"
|
||||
disabled
|
||||
class="btn-save mt-2 mr-2"
|
||||
waves
|
||||
data-loading-text="Guardando..." />
|
||||
<x-vuexy-admin::button.basic
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon="ti ti-rotate-2"
|
||||
label="Cancelar"
|
||||
wire:click="resetForm"
|
||||
class="btn-cancel mt-2 mr-2"
|
||||
waves />
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification-container pt-4" wire:ignore></div>
|
||||
</x-vuexy-admin::form.form>
|
||||
</div>
|
@ -0,0 +1,39 @@
|
||||
<div>
|
||||
<div id="logo-on-dark-bg-settings-card" class="mb-4">
|
||||
<x-vuexy-admin::card.basic title="Logotipo sobre fondo oscuro" class="mb-2">
|
||||
<x-vuexy-admin::form.input
|
||||
type="file"
|
||||
label="Logotipo sobre fondo oscuro"
|
||||
model="upload_image_logo_dark"
|
||||
accept="image/*" />
|
||||
<div class="mb-3 text-center align-items-center">
|
||||
<div class="justify-content-center align-items-center bg-slate-800 p-4">
|
||||
<img src="{{ $upload_image_logo_dark ? $upload_image_logo_dark->temporaryUrl() : asset('storage/' . $website_image_logo_dark) }}">
|
||||
</div>
|
||||
</div>
|
||||
</x-vuexy-admin::card.basic>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-end">
|
||||
<x-vuexy-admin::button.basic
|
||||
variant="primary"
|
||||
size="sm"
|
||||
icon="ti ti-device-floppy"
|
||||
disabled="{{ $upload_image_logo_dark === null }}"
|
||||
label="Guardar cambios"
|
||||
wire:click="save"
|
||||
class="btn-save mt-2 mr-2"
|
||||
waves />
|
||||
<x-vuexy-admin::button.basic
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon="ti ti-rotate-2"
|
||||
disabled="{{ $upload_image_logo_dark === null }}"
|
||||
label="Cancelar"
|
||||
wire:click="resetForm"
|
||||
class="btn-cancel mt-2 mr-2"
|
||||
waves />
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification-container pt-4" wire:ignore></div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,39 @@
|
||||
<div>
|
||||
<div id="logo-on-light-bg-settings-card" class="mb-4">
|
||||
<x-vuexy-admin::card.basic title="Logotipo sobre fondo claro" class="mb-2">
|
||||
<x-vuexy-admin::form.input
|
||||
type="file"
|
||||
label="Logotipo sobre fondo claro"
|
||||
model="upload_image_logo"
|
||||
accept="image/*" />
|
||||
<div class="mb-3 text-center align-items-center">
|
||||
<div class="justify-content-center align-items-center bg-slate-100 p-4">
|
||||
<img src="{{ $upload_image_logo ? $upload_image_logo->temporaryUrl() : asset('storage/' . $website_image_logo) }}">
|
||||
</div>
|
||||
</div>
|
||||
</x-vuexy-admin::card.basic>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-end">
|
||||
<x-vuexy-admin::button.basic
|
||||
variant="primary"
|
||||
size="sm"
|
||||
icon="ti ti-device-floppy"
|
||||
disabled="{{ $upload_image_logo === null }}"
|
||||
label="Guardar cambios"
|
||||
wire:click="save"
|
||||
class="btn-save mt-2 mr-2"
|
||||
waves />
|
||||
<x-vuexy-admin::button.basic
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon="ti ti-rotate-2"
|
||||
disabled="{{ $upload_image_logo === null }}"
|
||||
label="Cancelar"
|
||||
wire:click="resetForm"
|
||||
class="btn-cancel mt-2 mr-2"
|
||||
waves />
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification-container pt-4" wire:ignore></div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,47 @@
|
||||
<div>
|
||||
<x-vuexy-admin::form.form id="website-social-settings-card" class="form-custom-listener mb-4" whitOutId whitOutMode>
|
||||
<x-vuexy-admin::card.basic title="Redes sociales" class="mb-2">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<x-vuexy-admin::form.input model="social_whatsapp" label="WhatsApp" icon="ti ti-brand-whatsapp" placeholder="Enlace de WhatsApp" />
|
||||
<x-vuexy-admin::form.input model="social_whatsapp_message" placeholder="Mensaje de saludo WhatsApp" />
|
||||
<x-vuexy-admin::form.input model="social_facebook" label="Facebook" icon="ti ti-brand-facebook" placeholder="Enlace de Facebook" />
|
||||
<x-vuexy-admin::form.input model="social_instagram" label="Instagram" icon="ti ti-brand-instagram" placeholder="Enlace de Instagram" />
|
||||
<x-vuexy-admin::form.input model="social_linkedin" label="LinkedIn" icon="ti ti-brand-linkedin" placeholder="Enlace de LinkedIn" />
|
||||
<x-vuexy-admin::form.input model="social_tiktok" label="TikTok" icon="ti ti-brand-tiktok" placeholder="Enlace de TikTok" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<x-vuexy-admin::form.input model="social_x_twitter" label="X (Twitter)" icon="ti ti-brand-twitter" placeholder="Enlace de X (Twitter)" />
|
||||
<x-vuexy-admin::form.input model="social_google" label="Google" icon="ti ti-brand-google" placeholder="Enlace de Google" />
|
||||
<x-vuexy-admin::form.input model="social_pinterest" label="Pinterest" icon="ti ti-brand-pinterest" placeholder="Enlace de Pinterest" />
|
||||
<x-vuexy-admin::form.input model="social_youtube" label="YouTube" icon="ti ti-brand-youtube" placeholder="Enlace de YouTube" />
|
||||
<x-vuexy-admin::form.input model="social_vimeo" label="Vimeo" icon="ti ti-brand-vimeo" placeholder="Enlace de Vimeo" />
|
||||
</div>
|
||||
</div>
|
||||
</x-vuexy-admin::card.basic>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-end">
|
||||
<x-vuexy-admin::button.basic
|
||||
type="submit"
|
||||
variant="primary"
|
||||
size="sm"
|
||||
icon="ti ti-device-floppy"
|
||||
data-loading-text="Guardando..."
|
||||
label="Guardar cambios"
|
||||
disabled
|
||||
class="btn-save mt-2 mr-2"
|
||||
waves />
|
||||
<x-vuexy-admin::button.basic
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon="ti ti-rotate-2"
|
||||
disabled
|
||||
label="Cancelar"
|
||||
wire:click="resetForm"
|
||||
class="btn-cancel mt-2 mr-2"
|
||||
waves />
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification-container pt-4" wire:ignore></div>
|
||||
</x-vuexy-admin::form.form>
|
||||
</div>
|
49
resources/views/livewire/vuexy/template-settings.blade.php
Normal file
49
resources/views/livewire/vuexy/template-settings.blade.php
Normal file
@ -0,0 +1,49 @@
|
||||
<div>
|
||||
<form id="website-template-settings-card" novalidate="novalidate">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5>Porto Template 12.0.0</h5>
|
||||
<div class="mb-4">
|
||||
<x-form.checkbox
|
||||
name='website_tpl_style_switcher'
|
||||
wire:model.defer='website_tpl_style_switcher'
|
||||
parent_class='form-switch'>
|
||||
Mostrar personalizador de estilos
|
||||
</x-form.checkbox>
|
||||
</div>
|
||||
<div class="mb-4 fv-row">
|
||||
<label for="website_tpl_footer_text" class="form-label">Titulo de pie de página</label>
|
||||
<input type="text" id="website_tpl_footer_text" name="website_tpl_footer_text" wire:model='website_tpl_footer_text' class="form-control" placeholder="Titulo de pie de página">
|
||||
@error("website_tpl_footer_text")
|
||||
<span class="text-danger">{{ $message }}</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{{-- Botones --}}
|
||||
<div class="row my-4">
|
||||
<div class="col-lg-12 text-end">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary btn-save btn-sm mt-2 mr-2 waves-effect waves-light"
|
||||
disabled
|
||||
data-loading-text="Guardando...">
|
||||
<i class="ti ti-device-floppy mr-2"></i>
|
||||
Guardar cambios
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
wire:click="loadSettings"
|
||||
class="btn btn-secondary btn-cancel btn-sm mt-2 mr-2 waves-effect waves-light"
|
||||
disabled>
|
||||
<i class="ti ti-rotate-2 mr-2"></i>
|
||||
Cancelar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{-- Notifications --}}
|
||||
<div class="notification-container" wire:ignore></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
@ -0,0 +1,31 @@
|
||||
<div>
|
||||
<div id="website-description-settings-card" class="form-custom-listener mb-4">
|
||||
<x-vuexy-admin::card.basic title="Datos de la aplicación" class="mb-2">
|
||||
<x-vuexy-admin::form.input model="title" label="Titulo del sitio web" />
|
||||
<x-vuexy-admin::form.textarea model="description" label="Descripción del sitio web" />
|
||||
</x-vuexy-admin::card.basic>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-end">
|
||||
<x-vuexy-admin::button.basic
|
||||
variant="primary"
|
||||
size="sm"
|
||||
icon="ti ti-device-floppy"
|
||||
label="Guardar cambios"
|
||||
disabled
|
||||
wire:click="save"
|
||||
class="btn-save mt-2 mr-2"
|
||||
waves />
|
||||
<x-vuexy-admin::button.basic
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon="ti ti-rotate-2"
|
||||
disabled
|
||||
label="Cancelar"
|
||||
wire:click="resetForm"
|
||||
class="btn-cancel mt-2 mr-2"
|
||||
waves />
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification-container pt-4" wire:ignore></div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,84 @@
|
||||
<div>
|
||||
<div id="website-favicon-settings-card" class="mb-4">
|
||||
<x-vuexy-admin::card.basic title="Favicon" class="mb-2">
|
||||
<x-vuexy-admin::form.input
|
||||
type="file"
|
||||
label="Icono de navegador"
|
||||
model="upload_image_favicon"
|
||||
accept="image/*" />
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
||||
<div class="text-center flex flex-col items-center">
|
||||
<div class="mb-3 text-center d-flex flex-column align-items-center">
|
||||
<div class="image-wrapper-16x16 d-flex justify-content-center align-items-center">
|
||||
<img src="{{ $upload_image_favicon ? $upload_image_favicon->temporaryUrl() : asset('storage/' . $website_favicon_16x16) }}">
|
||||
</div>
|
||||
<span class="text-muted mt-1">Navegadores web (16x16)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center flex flex-col items-center">
|
||||
<div class="mb-3 text-center d-flex flex-column align-items-center">
|
||||
<div class="image-wrapper-76x76 d-flex justify-content-center align-items-center">
|
||||
<img src="{{ $upload_image_favicon ? $upload_image_favicon->temporaryUrl() : asset('storage/' . $website_favicon_76x76) }}">
|
||||
</div>
|
||||
<span class="text-muted mt-1">iPad sin Retina (76x76)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center flex flex-col items-center">
|
||||
<div class="mb-3 text-center d-flex flex-column align-items-center">
|
||||
<div class="image-wrapper-120x120 d-flex justify-content-center align-items-center">
|
||||
<img src="{{ $upload_image_favicon ? $upload_image_favicon->temporaryUrl() : asset('storage/' . $website_favicon_120x120) }}">
|
||||
</div>
|
||||
<span class="text-muted mt-1">iPhone (120x120)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center flex flex-col items-center">
|
||||
<div class="mb-3 text-center d-flex flex-column align-items-center">
|
||||
<div class="image-wrapper-152x152 d-flex justify-content-center align-items-center">
|
||||
<img src="{{ $upload_image_favicon ? $upload_image_favicon->temporaryUrl() : asset('storage/' . $website_favicon_152x152) }}">
|
||||
</div>
|
||||
<span class="text-muted mt-1">iPad (152x152)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center flex flex-col items-center">
|
||||
<div class="mb-3 text-center d-flex flex-column align-items-center">
|
||||
<div class="image-wrapper-180x180 d-flex justify-content-center align-items-center">
|
||||
<img src="{{ $upload_image_favicon ? $upload_image_favicon->temporaryUrl() : asset('storage/' . $website_favicon_180x180) }}">
|
||||
</div>
|
||||
<span class="text-muted mt-1">iPhone con Retina HD (180x180)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center flex flex-col items-center">
|
||||
<div class="mb-3 text-center d-flex flex-column align-items-center">
|
||||
<div class="image-wrapper-192x192 d-flex justify-content-center align-items-center">
|
||||
<img src="{{ $upload_image_favicon ? $upload_image_favicon->temporaryUrl() : asset('storage/' . $website_favicon_192x192) }}">
|
||||
</div>
|
||||
<span class="text-muted mt-1">Android y otros dispositivos móviles (192x192)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-vuexy-admin::card.basic>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-end">
|
||||
<x-vuexy-admin::button.basic
|
||||
variant="primary"
|
||||
size="sm"
|
||||
icon="ti ti-device-floppy"
|
||||
label="Guardar cambios"
|
||||
wire:click="save"
|
||||
:disabled="$upload_image_favicon === null"
|
||||
class="btn-save mt-2 mr-2"
|
||||
waves />
|
||||
<x-vuexy-admin::button.basic
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon="ti ti-rotate-2"
|
||||
label="Cancelar"
|
||||
wire:click="resetForm"
|
||||
:disabled="$upload_image_favicon === null"
|
||||
class="btn-cancel mt-2 mr-2"
|
||||
waves />
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification-container pt-4" wire:ignore></div>
|
||||
</div>
|
||||
</div>
|
7
resources/views/sitemap-manager/index.blade.php
Normal file
7
resources/views/sitemap-manager/index.blade.php
Normal file
@ -0,0 +1,7 @@
|
||||
@extends('vuexy-admin::layouts.vuexy.layoutMaster')
|
||||
|
||||
@section('title', 'Mapa del Sitio')
|
||||
|
||||
@section('content')
|
||||
@livewire('vuexy-website-admin::sitemap-manager-index')
|
||||
@endsection
|
25
resources/views/social-media/index.blade.php
Normal file
25
resources/views/social-media/index.blade.php
Normal file
@ -0,0 +1,25 @@
|
||||
@extends('vuexy-admin::layouts.vuexy.layoutMaster')
|
||||
|
||||
@section('title', 'Redes Sociales')
|
||||
|
||||
@section('vendor-style')
|
||||
@vite([
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/form-validation.scss'
|
||||
])
|
||||
@endsection
|
||||
|
||||
@section('vendor-script')
|
||||
@vite([
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/popular.js',
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/bootstrap5.js',
|
||||
'vendor/koneko/laravel-vuexy-admin/resources/assets/vendor/libs/@form-validation/auto-focus.js',
|
||||
])
|
||||
@endsection
|
||||
|
||||
@push('page-script')
|
||||
@vite('vendor/koneko/laravel-vuexy-website-admin/resources/js/website-settings-card.js')
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
@livewire('vuexy-website-admin::social-media-settings')
|
||||
@endsection
|
@ -1,9 +1,59 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Koneko\VuexyAdmin\Http\Controllers\UUserController;
|
||||
use Koneko\VuexyWebsiteAdmin\Http\Controllers\{LegalNoticesController,FaqController,ImagesController};
|
||||
use Koneko\VuexyWebsiteAdmin\Http\Controllers\{SocialMediaController,ChatController,GoogleAnalyticsController};
|
||||
use Koneko\VuexyWebsiteAdmin\Http\Controllers\{ContactInfoController,ContactFormController,VuexyWebsiteAdminController,SitemapController};
|
||||
|
||||
// Grupo raíz para admin con middleware y prefijos comunes
|
||||
Route::prefix('admin')->name('admin.')->middleware(['web', 'auth', 'admin.settings'])->group(function () {
|
||||
Route::prefix('admin/sitio-web')->name('admin.website-admin.')->middleware(['web', 'auth', 'admin'])->group(function () {
|
||||
// ajustes generales
|
||||
Route::controller(VuexyWebsiteAdminController::class)->prefix('ajustes-generales')->group(function () {
|
||||
Route::get('ajustes-generales', 'index')->name('general-settings.index');
|
||||
});
|
||||
|
||||
// Avisos legales
|
||||
Route::controller(LegalNoticesController::class)->prefix('avisos-legales')->group(function () {
|
||||
Route::get('/', 'index')->name('legal-notices.index');
|
||||
});
|
||||
|
||||
// Preguntas frecuentes
|
||||
Route::controller(FaqController::class)->prefix('preguntas-frecuentes')->group(function () {
|
||||
Route::get('/', 'index')->name('faq.index');
|
||||
});
|
||||
|
||||
// Redes sociales
|
||||
Route::controller(SocialMediaController::class)->prefix('redes-sociales')->group(function () {
|
||||
Route::get('/', 'index')->name('social-media.index');
|
||||
});
|
||||
|
||||
// Chat
|
||||
Route::controller(ChatController::class)->prefix('chat')->group(function () {
|
||||
Route::get('/', 'index')->name('chat.index');
|
||||
});
|
||||
|
||||
// Galería de imágenes
|
||||
Route::controller(ImagesController::class)->prefix('galeria-de-imagenes')->group(function () {
|
||||
Route::get('/', 'index')->name('images.index');
|
||||
});
|
||||
|
||||
// Google Analytics
|
||||
Route::controller(GoogleAnalyticsController::class)->prefix('google-analytics')->group(function () {
|
||||
Route::get('/', 'index')->name('google-analytics.index');
|
||||
});
|
||||
|
||||
// Información de contacto
|
||||
Route::controller(ContactInfoController::class)->prefix('informacion-de-contacto')->group(function () {
|
||||
Route::get('/', 'index')->name('contact-info.index');
|
||||
});
|
||||
|
||||
// Formulario de contacto
|
||||
Route::controller(ContactFormController::class)->prefix('formulario-de-contacto')->group(function () {
|
||||
Route::get('/', 'index')->name('contact-form.index');
|
||||
});
|
||||
|
||||
// Mapa del sitio
|
||||
Route::controller(SitemapController::class)->prefix('mapa-del-sitio')->group(function () {
|
||||
Route::get('/', 'index')->name('sitemap.index');
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user