first commit

This commit is contained in:
2025-03-05 20:43:35 -06:00
commit aa938a3cab
47 changed files with 4388 additions and 0 deletions

View File

@ -0,0 +1,211 @@
<?php
namespace Koneko\VuexyStoreManager\Livewire\WorkCenters;
use Koneko\VuexyAdmin\Livewire\Table\AbstractIndexComponent;
use Koneko\VuexyStoreManager\Models\StoreWorkCenter;
use Koneko\VuexyStoreManager\Services\StoreCatalogService;
/**
* Index para Centros de Trabajo, extendiendo la clase base AbstractIndexComponent.
*/
class WorkCenterIndex extends AbstractIndexComponent
{
/**
* Usado para filtrar (opcional) o cargar catálogos en la vista.
*/
public $store_id;
/**
* Opciones de tiendas (por ejemplo, para un select de filtrado).
*/
public $storeOptions = [];
/**
* Montamos (inicializamos) el componente y llamamos al mount() del padre
* para configurar la tabla y setear $tagName, $singularName, $formId, etc.
*/
public function mount(): void
{
parent::mount();
// Ahora cargamos las opciones de tienda, usando el servicio necesario.
$storeCatalogService = app(StoreCatalogService::class);
$this->storeOptions = $storeCatalogService->searchCatalog('stores', '', ['limit' => -1]);
}
/**
* Indica la clase (o instancia) de tu modelo.
*
* @return string
*/
protected function model(): string
{
// Retornamos la clase del modelo
return StoreWorkCenter::class;
}
/**
* Retorna las columnas (header) de tu tabla.
*
* @return array
*/
protected function columns(): array
{
return [
'action' => 'Acciones',
'stores_code' => 'Código de Tienda',
'stores_name' => 'Nombre de la Tienda',
'code' => 'Código del Centro',
'name' => 'Nombre del Centro',
'description' => 'Descripción',
'manager_name' => 'Gerente',
'tel' => 'Teléfono',
'tel2' => 'Teléfono Alternativo',
'codigo_postal' => 'Código Postal',
'pais' => 'País',
'estado' => 'Estado',
'localidad' => 'Localidad',
'municipio' => 'Municipio',
'colonia' => 'Colonia',
'direccion' => 'Dirección',
'lat' => 'Latitud',
'lng' => 'Longitud',
'status' => 'Estatus',
'created_at' => 'Creado',
'updated_at' => 'Actualizado',
];
}
/**
* Retorna el formato (formatter) de las columnas.
*
* @return array
*/
protected function format(): array
{
return [
'action' => [
'formatter' => 'workCenterActionFormatter',
'onlyFormatter' => true,
],
'stores_code' => [
'formatter' => [
'name' => 'dynamicBadgeFormatter',
'params' => ['color' => 'secondary'],
],
'align' => 'center',
],
'stores_name' => [
'visible' => false,
],
'code' => [
'formatter' => [
'name' => 'dynamicBadgeFormatter',
'params' => ['color' => 'secondary'],
],
'align' => 'center',
'switchable' => false,
],
'name' => [
'switchable' => false,
],
'description' => [
'visible' => false,
],
'manager_name' => [
'formatter' => 'managerFormatter',
],
'tel' => [
'formatter' => 'telFormatter',
'align' => 'center',
],
'tel2' => [
'formatter' => 'telFormatter',
'align' => 'center',
'visible' => false,
],
'pais' => [
'align' => 'center',
'visible' => false,
],
'estado' => [
'formatter' => 'textNowrapFormatter',
'visible' => false,
],
'localidad' => [
'formatter' => 'textNowrapFormatter',
'visible' => false,
],
'municipio' => [
'formatter' => 'textNowrapFormatter',
'visible' => false,
],
'codigo_postal' => [
'align' => 'center',
'visible' => false,
],
'colonia' => [
'formatter' => 'textNowrapFormatter',
'visible' => false,
],
'direccion' => [
'formatter' => 'direccionFormatter',
'visible' => false,
],
'lat' => [
'align' => 'center',
'visible' => false,
],
'lng' => [
'align' => 'center',
'visible' => false,
],
'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,
],
];
}
/**
* Sobrescribe la config base para adaptarla al caso de los Centros de Trabajo.
*
* @return array
*/
protected function bootstraptableConfig(): array
{
// Llamamos al padre y luego ajustamos lo que necesitemos.
return array_merge(parent::bootstraptableConfig(), [
'sortName' => 'code',
'exportFileName' => 'Centros de Trabajo',
'showFullscreen' => false,
'showPaginationSwitch'=> false,
'showRefresh' => false,
'pagination' => false,
]);
}
/**
* Retorna la vista que se usará para renderizar este componente.
*
* @return string
*/
protected function viewPath(): string
{
return 'vuexy-store-manager::livewire.work-center.index';
}
}

View File

@ -0,0 +1,202 @@
<?php
namespace Koneko\VuexyStoreManager\Livewire\Workcenters;
use Illuminate\Validation\Rule;
use Koneko\VuexyAdmin\Livewire\Form\AbstractFormOffCanvasComponent;
use Koneko\VuexyContacts\Services\ContactCatalogService;
use Koneko\VuexyStoreManager\Models\StoreWorkCenter;
use Koneko\VuexyStoreManager\Services\StoreCatalogService;
/**
* Class WorkCenterOffcanvasForm
*
* Componente Livewire para gestionar centros de trabajo.
* Extiende las funcionalidades generales de AbstractFormOffCanvasComponent.
*
* @package Koneko\VuexyStoreManager\Livewire\Workcenters
*/
class WorkCenterOffcanvasForm extends AbstractFormOffCanvasComponent
{
/**
* Campos específicos del centro de trabajo.
*/
public $store_id, $code, $name, $description, $manager_id, $tel, $tel2, $lat, $lng, $status;
/**
* Listas de opciones para selects en el formulario.
*/
public $store_options = [],
$manager_options = [];
/**
* Eventos de escucha de Livewire.
*
* @var array
*/
protected $listeners = [
'editWorkCenter' => 'loadFormModel',
'confirmDeletionWorkCenter' => 'loadFormModelForDeletion',
];
/**
* Definición de tipos de datos que se deben castear.
*
* @var array
*/
protected $casts = [
//
'status' => 'boolean',
];
/**
* Devuelve el modelo relacionado con el formulario.
*
* @return string
*/
protected function model(): string
{
return StoreWorkCenter::class;
}
/**
* Define los campos del formulario.
*
* @return array<string, mixed>
*/
protected function fields(): array
{
return (new StoreWorkCenter())->getFillable();
}
/**
* Valores por defecto para el formulario.
*
* @return array
*/
protected function defaults(): array
{
return [
'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'],
'code' => [
'required', 'string', 'max:16',
'regex:/^[a-zA-Z0-9_-]+$/', // Solo alfanuméricos, guiones y guiones bajos
Rule::unique('store_work_centers', 'code')->ignore($this->id),
],
'name' => [
'required', 'string', 'max:96',
'regex:/^[a-zA-ZáéíóúÁÉÍÓÚñÑ0-9\s-]+$/', // Solo letras, números y espacios
Rule::unique('store_work_centers')
->where(fn ($query) => $query->where('store_id', $this->store_id))
->ignore($this->id),
],
'description' => ['nullable', 'string', 'max:1024'],
'manager_id' => ['nullable', 'integer', 'exists:users,id'],
'tel' => ['nullable', 'regex:/^\+?[0-9()\s-]+$/', 'max:20'], // Permitir formatos internacionales
'tel2' => ['nullable', 'regex:/^\+?[0-9()\s-]+$/', 'max:20'],
'lat' => ['nullable', 'numeric', 'between:-90,90'],
'lng' => ['nullable', 'numeric', 'between:-180,180'],
'status' => ['nullable', 'boolean']
];
case 'delete':
return [
'confirmDeletion' => 'accepted', // Confirma que el usuario acepta eliminar
];
default:
return [];
}
}
// ===================== VALIDACIONES =====================
/**
* Get custom attributes for validator errors.
*
* @return array<string, string>
*/
protected function attributes(): array
{
return [
'store_id' => 'negocio',
'code' => 'código del centro de trabajo',
'name' => 'nombre del centro de trabajo',
'tel' => 'teléfono',
'tel2' => 'teléfono alternativo',
'lat' => 'latitud',
'lng' => 'longitud',
'status' => 'estado',
];
}
/**
* Get the error messages for the defined validation rules.
*
* @return array<string, string>
*/
protected function messages(): array
{
return [
'store_id.required' => 'El centro de trabajo debe estar asociado a un negocio.',
'code.required' => 'El código del centro de trabajo es obligatorio.',
'code.unique' => 'Este código ya está en uso por otro centro de trabajo.',
'name.required' => 'El nombre del centro de trabajo es obligatorio.',
'name.unique' => 'Ya existe un centro de trabajo con este nombre en este negocio.',
'name.regex' => 'El nombre solo puede contener letras, números, espacios y guiones.',
];
}
/**
* 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-store-manager::livewire.work-center.form-offcanvas';
}
}