Merge branch 'dev/chat' into 'main'

Dev/chat

See merge request jjimenez/safekat!336
This commit is contained in:
Alvaro
2024-09-23 08:10:57 +00:00
39 changed files with 2916 additions and 33 deletions

View File

@ -721,6 +721,44 @@ $routes->group(
);
$routes->resource('buscadorpresupuestos', ['namespace' => 'App\Controllers\Presupuestos', 'controller' => 'Buscador', 'except' => 'show,new,create,update']);
/* Rutas para mensajeria */
$routes->group('mensajes', ['namespace' => 'App\Controllers\Mensajeria'], function ($routes) {
/* Interna */
$routes->group('internos', ['namespace' => 'App\Controllers\Mensajeria'], function ($routes) {
$routes->get('', 'MensajesDirectos::index', ['as' => 'mensajeriaView']);
$routes->get('chat/(:num)', 'MensajesDirectos::getChatInfo/$1', ['as' => 'getChatInfo']);
/*$routes->match(['get', 'post'], 'add', 'TarifaAcabados::add', ['as' => 'tarifaAcabadoAdd']);
$routes->match(['get', 'post'], 'edit/(:num)', 'TarifaAcabados::edit/$1', ['as' => 'tarifaAcabadoEdit']);
$routes->get('delete/(:num)', 'TarifaAcabados::delete/$1', ['as' => 'tarifaAcabadoDelete']);
$routes->post('datatable', 'TarifaAcabados::datatable', ['as' => 'tarifaAcabadoDT']);*/
});
});
$routes->group('chat', ['namespace' => 'App\Controllers\Chat'], function ($routes) {
$routes->get('departments', 'ChatController::get_chat_departments', ['as' => 'getChatDepartments']);
$routes->get('department/presupuesto/(:num)/(:num)', 'ChatController::get_chat_presupuesto/$1/$2', ['as' => 'getChatPresupuesto']);
$routes->get('department/pedido/(:num)/(:num)', 'ChatController::get_chat_pedido/$1/$2', ['as' => 'getChatPedido']);
$routes->get('department/factura/(:num)/(:num)', 'ChatController::get_chat_factura/$1/$2', ['as' => 'getChatFactura']);
$routes->get('(:num)', 'ChatController::get_chat/$1', ['as' => 'getChat']);
$routes->post('message/presupuesto', 'ChatController::store_chat_message_presupuesto', ['as' => 'storeChatMessagePresupuesto']);
$routes->post('message/pedido', 'ChatController::store_chat_message_pedido', ['as' => 'storeChatMessagePedido']);
$routes->post('message/factura', 'ChatController::store_chat_message_factura', ['as' => 'storeChatMessageFactura']);
$routes->post('message/internal', 'ChatController::store_chat_message_single', ['as' => 'storeChatMessageSingle']);
$routes->get('contacts', 'ChatController::get_chat_internal_contacts', ['as' => 'getChatInternalContacts']);
$routes->get('contacts/(:num)', 'ChatController::get_chat_internal_contact/$1', ['as' => 'getChatInternalContact']);
$routes->get('contact/(:num)/messages', 'ChatController::get_chat_internal_messages/$1', ['as' => 'getChatInternalMessages']);
});
/*
* --------------------------------------------------------------------

View File

@ -0,0 +1,189 @@
<?php
namespace App\Controllers\Chat;
use App\Controllers\BaseController;
use App\Models\Chat\ChatDeparmentModel;
use App\Models\Chat\ChatDeparmentUserModel;
use App\Models\Chat\ChatMessageModel;
use App\Models\Chat\ChatModel;
use App\Models\Usuarios\UserModel;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\Log\Logger;
use Psr\Log\LoggerInterface;
class ChatController extends BaseController
{
protected ChatDeparmentModel $chatDeparmentModel;
protected ChatDeparmentUserModel $chatDeparmentUserModel;
protected ChatModel $chatModel;
protected ChatMessageModel $chatMessageModel;
protected UserModel $userModel;
public function initController(
RequestInterface $request,
ResponseInterface $response,
LoggerInterface $logger
) {
parent::initController($request, $response, $logger);
// Add your code here.
$this->chatDeparmentModel = model(ChatDeparmentModel::class);
$this->chatDeparmentUserModel = model(ChatDeparmentUserModel::class);
$this->chatModel = model(ChatModel::class);
$this->chatMessageModel = model(ChatMessageModel::class);
$this->userModel = model(UserModel::class);
}
public function index() {}
public function get_chat_departments()
{
$data = $this->chatDeparmentModel->getChatDepartments();
return $this->response->setJSON($data);
}
public function get_chat_presupuesto(int $chat_department_id, int $presupuesto_id)
{
$data = [
"chat" => null,
"messages" => null,
];
$chat = $this->chatModel->getChatPresupuesto($chat_department_id, $presupuesto_id);
if ($chat) {
$data["messages"] = $this->chatMessageModel->get_chat_messages($chat->id);
}
$data["chat"] = $chat;
return $this->response->setJSON($data);
}
public function get_chat_pedido(int $chat_department_id, int $pedido_id)
{
$data = [
"chat" => null,
"messages" => null,
];
$chat = $this->chatModel->getChatPedido($chat_department_id, $pedido_id);
if ($chat) {
$data["messages"] = $this->chatMessageModel->get_chat_messages($chat->id);
}
$data["chat"] = $chat;
return $this->response->setJSON($data);
}
public function get_chat_factura(int $chat_department_id, int $factura_id)
{
$data = [
"chat" => null,
"messages" => null,
];
$chat = $this->chatModel->getChatFactura($chat_department_id, $factura_id);
if ($chat) {
$data["messages"] = $this->chatMessageModel->get_chat_messages($chat->id);
}
$data["chat"] = $chat;
return $this->response->setJSON($data);
}
public function get_chat(int $chat_id)
{
$data = $this->chatModel->getChat($chat_id);
return $this->response->setJSON($data);
}
public function store_chat_message_presupuesto()
{
$data = $this->request->getPost();
// $data = $this->chatModel->createChatPresupuesto();
$existChat = $this->chatModel->existChatPresupuesto($data["chat_department_id"], $data["model_id"]);
if ($existChat == false) {
$chatId = $this->chatModel->createChatPresupuesto($data["chat_department_id"], $data["model_id"]);
} else {
$chat = $this->chatModel->getChatPresupuesto($data["chat_department_id"], $data["model_id"]);
$chatId = $chat->id;
}
$chat_message_id = $this->chatMessageModel->insert(["chat_id" => $chatId, "sender_id" => auth()->user()->id, "message" => $data["message"]]);
$dataResponse = $this->chatMessageModel->find($chat_message_id);
return $this->response->setJSON($dataResponse);
}
public function store_chat_message_pedido()
{
$data = $this->request->getPost();
$existChat = $this->chatModel->existChatPedido($data["chat_department_id"], $data["model_id"]);
if ($existChat == false) {
$chatId = $this->chatModel->createChatPedido($data["chat_department_id"], $data["model_id"]);
} else {
$chat = $this->chatModel->getChatPedido($data["chat_department_id"], $data["model_id"]);
$chatId = $chat->id;
}
$chat_message_id = $this->chatMessageModel->insert(["chat_id" => $chatId, "sender_id" => auth()->user()->id, "message" => $data["message"]]);
$dataResponse = $this->chatMessageModel->find($chat_message_id);
return $this->response->setJSON($dataResponse);
}
public function store_chat_message_factura()
{
$data = $this->request->getPost();
$existChat = $this->chatModel->existChatFactura($data["chat_department_id"], $data["model_id"]);
if ($existChat == false) {
$chatId = $this->chatModel->createChatFactura($data["chat_department_id"], $data["model_id"]);
} else {
$chat = $this->chatModel->getChatFactura($data["chat_department_id"], $data["model_id"]);
$chatId = $chat->id;
}
$chat_message_id = $this->chatMessageModel->insert(["chat_id" => $chatId, "sender_id" => auth()->user()->id, "message" => $data["message"]]);
$dataResponse = $this->chatMessageModel->find($chat_message_id);
return $this->response->setJSON($dataResponse);
}
public function store_chat_message_single()
{
$data = $this->request->getPost();
$existChat = $this->chatMessageModel->get_chat_contact_messages($data["receiver_id"]);
if (count($existChat) > 0) {
$chatId = $existChat[0]->chat_id;
} else {
$chatId = $this->chatModel->createChatSingle();
}
$chat_message_id = $this->chatMessageModel->insert(
[
"chat_id" => $chatId,
"sender_id" => auth()->user()->id,
"message" => $data["message"],
"receiver_id" => $data["receiver_id"]
]
);
$dataResponse = $this->chatMessageModel->find($chat_message_id);
return $this->response->setJSON($dataResponse);
}
public function get_chat_internal_contacts()
{
$users = $this->userModel->builder()
->where("cliente_id", null)
->whereNotIn("id", [auth()->user()->id])
->where("deleted_at", null)
->get()->getResultObject();
foreach ($users as $user) {
$user->unreadMessages = $this->chatMessageModel->get_chat_unread_messages_count($user->id);
}
return $this->response->setJSON($users);
}
public function get_chat_internal_contact(int $user_id)
{
$users = $this->userModel->builder()
->where("cliente_id", null)
->where("deleted_at", null)
->where("id", $user_id)
->get()->getFirstRow();
$this->chatMessageModel->set_chat_messages_as_read($user_id);
return $this->response->setJSON($users);
}
public function get_chat_internal_messages(int $user_id)
{
$conversation = $this->chatMessageModel->get_chat_contact_messages($user_id);
return $this->response->setJSON($conversation);
}
}

View File

@ -117,5 +117,13 @@ class Js_loader extends BaseController
$this->response->setHeader('Content-Type', 'text/javascript');
return view('themes/vuexy/form/presupuestos/cliente/previews.js');
}
function chat_js()
{
$this->response->setHeader('Content-Type', 'text/javascript');
return view('themes/vuexy/form/mensajes/mensajeria.js');
}
}

View File

@ -0,0 +1,64 @@
<?php namespace App\Controllers\Mensajeria;
use App\Controllers\BaseResourceController;
use App\Entities\Mensajeria\ConversacionEntity;
use App\Entities\Sistema\ActivityEntity;
use App\Models\CollectionModel;
use App\Models\Mensajeria\ConversacionModel;
use App\Models\Sistema\ActivityModel;
class MensajesDirectos extends BaseResourceController
{
protected $modelName = ConversacionModel::class;
protected $format = 'json';
protected static $controllerSlug = 'mensajes-internos';
protected static $viewPath = 'themes/vuexy/form/mensajes/';
protected static $indexRoute = 'mensajesView';
public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
{
$this->viewData['pageTitle'] = "Mensajeria interna";
// Breadcrumbs
$this->viewData['breadcrumb'] = [
['title' => "Home", 'route' => "javascript:void(0);", 'active' => false],
['title' => lang("App.menu_mensajes"), 'route' => route_to('mensajeriaView'), 'active' => true]
];
parent::initController($request, $response, $logger);
}
public function index()
{
return view(static::$viewPath . static::$indexRoute);
}
public function getChatInfo($conversacionId)
{
// Modelos
$conversacionModel = model('App\Models\Mensajeria\ConversacionModel');
// Verificar si es una solicitud AJAX
if ($this->request->isAJAX()) {
// Obtener los datos
$data = [
'people' => $conversacionModel->getChatParticipants($conversacionId),
'messages' => $conversacionModel->getChatMessages($conversacionId)
];
// Devolver respuesta JSON
return $this->respond($data, 200, 'Chat information retrieved successfully');
} else {
return $this->failForbidden('Only AJAX requests are allowed');
}
}
}

View File

@ -76,10 +76,10 @@ class Test extends BaseController
private function test_get_tirada_alt($tirada, $merma, $tipo_impresion_id,
$json_data, $cliente_id, $ancho, $alto,
$solapas_cubierta, $solapas_ancho_cubierta, $solapas_sobrecubierta, $solapas_ancho_sobrecubierta, $lomo)
{
private function test_get_tirada_alt($tirada, $merma, $tipo_impresion_id,
$json_data, $cliente_id, $ancho, $alto,
$solapas_cubierta, $solapas_ancho_cubierta, $solapas_sobrecubierta, $solapas_ancho_sobrecubierta, $lomo)
{
$values = [];
if ($json_data) {

View File

@ -0,0 +1,66 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
use CodeIgniter\Database\RawSql;
class ChatDepartments extends Migration
{
protected array $COLUMNS = [
"id" => [
"type" => "INT",
"unsigned" => true,
"auto_increment" => true
],
"name" => [
"type" => "VARCHAR",
"constraint" => '45',
"unique" => true,
],
"display" => [
"type" => "VARCHAR",
"constraint" => '255',
],
"description" => [
"type" => "TEXT",
"null" => true,
],
"type" => [
"type" => "ENUM",
'constraint' => ['general', 'presupuesto', 'pedido'],
'default' => 'general',
],
];
public function up()
{
$this->forge->addField($this->COLUMNS);
$currenttime = new RawSql("CURRENT_TIMESTAMP");
$this->forge->addField([
"created_at" => [
"type" => "TIMESTAMP",
"default" => $currenttime,
],
"updated_at" => [
"type" => "TIMESTAMP",
"null" => true,
],
"deleted_at" => [
"type" => "TIMESTAMP",
"null" => true,
],
]);
$this->forge->addPrimaryKey('id');
$this->forge->createTable("chat_departments", true);
}
public function down()
{
//
$this->forge->dropTable("chat_departments");
}
}

View File

@ -0,0 +1,81 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\RawSql;
use CodeIgniter\Database\Migration;
class ChatsTable extends Migration
{
protected array $COLUMNS = [
"id" => [
"type" => "INT",
"unsigned" => true,
"auto_increment" => true
],
"chat_department_id" => [
"type" => "INT",
"unsigned" => true,
"null" => true
],
"pedido_id" => [
"type" => "INT",
"constraint" => 16,
"unsigned" => true,
"null" => true
],
"presupuesto_id" => [
"type" => "INT",
"constraint" => 10,
"unsigned" => true,
"null" => true
],
"factura_id" => [
"type" => "INT",
"constraint" => 10,
"unsigned" => true,
"null" => true
],
];
public function up()
{
$this->forge->dropTable("chat_conversaciones", true);
$this->forge->dropTable("chat_mensajes", true);
$this->forge->dropTable("chat_participantes", true);
$this->forge->addField($this->COLUMNS);
$currenttime = new RawSql("CURRENT_TIMESTAMP");
$this->forge->addField([
"created_at" => [
"type" => "TIMESTAMP",
"default" => $currenttime,
],
"updated_at" => [
"type" => "TIMESTAMP",
"null" => true,
],
"deleted_at" => [
"type" => "TIMESTAMP",
"null" => true,
],
]);
$this->forge->addPrimaryKey('id');
$this->forge->addForeignKey('pedido_id', 'pedidos', 'id');
$this->forge->addForeignKey('chat_department_id', 'chat_departments', 'id');
$this->forge->addForeignKey('factura_id', 'facturas', 'id');
$this->forge->addForeignKey('presupuesto_id', 'presupuestos', 'id');
$this->forge->createTable("chats", true);
}
public function down()
{
//
$this->forge->dropTable("chats", true);
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
use CodeIgniter\Database\RawSql;
class ChatMessages extends Migration
{
protected array $COLUMNS = [
"id" => [
"type" => "INT",
"auto_increment" => true,
],
"chat_id" => [
"type" => "INT",
"unsigned" => true
],
"sender_id" => [
"type" => "INT",
"unsigned" => true,
"null" => true,
],
"receiver_id" => [
"type" => "INT",
"unsigned" => true,
"null" => true,
],
"message" => [
"type" => "TEXT",
],
"viewed" => [
"type" => "TEXT",
"type" => "boolean",
"default" => false,
],
];
public function up()
{
$this->forge->addField($this->COLUMNS);
$currenttime = new RawSql("CURRENT_TIMESTAMP");
$this->forge->addField([
"created_at" => [
"type" => "TIMESTAMP",
"default" => $currenttime,
],
"updated_at" => [
"type" => "TIMESTAMP",
"null" => true,
],
"deleted_at" => [
"type" => "TIMESTAMP",
"null" => true,
],
]);
$this->forge->addPrimaryKey('id');
$this->forge->addForeignKey(['receiver_id'], 'users', ['id']);
$this->forge->addForeignKey(['sender_id'], 'users', ['id']);
$this->forge->addForeignKey(['chat_id'], 'chats', ['id']);
$this->forge->createTable("chat_messages", true);
}
public function down()
{
$this->forge->dropTable("chat_messages", true);
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
use CodeIgniter\Database\RawSql;
class ChatDepartmentUsers extends Migration
{
protected array $COLUMNS = [
"chat_department_id" => [
"type" => "INT",
"unsigned" => true,
],
"user_id" => [
"type" => "INT",
"unsigned" => true,
],
];
public function up()
{
$this->forge->addField($this->COLUMNS);
$currenttime = new RawSql("CURRENT_TIMESTAMP");
$this->forge->addField([
"created_at" => [
"type" => "TIMESTAMP",
"default" => $currenttime,
],
"updated_at" => [
"type" => "TIMESTAMP",
"null" => true,
],
"deleted_at" => [
"type" => "TIMESTAMP",
"null" => true,
],
]);
$this->forge->addForeignKey(["user_id"], "users", ["id"]);
$this->forge->addForeignKey(["chat_department_id"], "chat_departments", ["id"]);
$this->forge->createTable("chat_department_users", true);
}
public function down()
{
$this->forge->dropTable("chat_department_users");
}
}

View File

@ -0,0 +1,89 @@
<?php
namespace App\Database\Seeds;
use App\Models\Chat\ChatDeparmentModel;
use App\Models\Chat\ChatDeparmentUserModel;
use CodeIgniter\Database\Seeder;
use App\Models\Usuarios\UserModel;
class ChatSeeder extends Seeder
{
public function run()
{
$data = [
[
"name" => "_produccion",
"display" => "Producción",
"users" => [
"mbalbaci@safekat.com",
"mari.cano@safekat.com",
"beatriz@safekat.com",
"imnavajas@coit.es",
],
],
[
"name" => "_pod",
"display" => "POD",
"users" => [
"pod@safekat.com",
"imnavajas@coit.es",
],
],
[
"name" => "_maquetacion",
"display" => "Maquetación",
"users" => [
"maquetacion@safekat.com",
"imnavajas@coit.es",
],
],
// [
// "name" => "_comercial",
// "display" => "Comercial",
// "users" => [
// "incidencias@safekat.com",
// ],
// ],
[
"name" => "_incidencias",
"display" => "Incidencias",
"users" => [
"incidencias@safekat.com",
],
],
[
"name" => "_logistica",
"display" => "Logística",
"users" => [
"logistica@safekat.com",
"imnavajas@coit.es",
],
],
[
"name" => "_admin",
"display" => "Administración",
"users" => [
"contabilidad@safekat.com",
"imnavajas@coit.es",
],
],
];
$chatDeparmentModel = model(ChatDeparmentModel::class);
$chatDeparmentUsersModel = model(ChatDeparmentUserModel::class);
$userModel = model(UserModel::class);
foreach ($data as $row) {
$chatDeparmentId = $chatDeparmentModel->insert(["name" => $row["name"], "display" => $row["display"]]);
if (count($row["users"]) > 0) {
foreach ($row["users"] as $mail) {
$user = $userModel->like("username", explode("@",$mail)[0])->first();
if ($user) {
echo $user->id."\r\n";
$chatDeparmentUsersModel->insert(['user_id' => $user->id, "chat_department_id" => $chatDeparmentId]);
}
}
}
};
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Entities\Mensajeria;
use CodeIgniter\Entity\Entity;
class ConversacionEntity extends Entity
{
protected $attributes = [
'id' => null,
'pedido_libro_id' => null,
'pedido_maquetacion_id' => null,
'factura_id' => null,
'departamento' => null,
'asunto' => null,
'created_at' => null,
'updated_at' => null,
'deleted_at' => null,
];
protected $casts = [
"pedido_libro_id" => "?int",
"pedido_maquetacion_id" => "?int",
"factura_id" => "?int"
];
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Entities\Mensajeria;
use CodeIgniter\Entity\Entity;
class ParticipanteEntity extends Entity
{
protected $attributes = [
'id' => null,
'conversacion_id' => null,
'usuario_id' => null,
'cliente_id' => null,
'email' => null,
'last_read' => null,
'created_at' => null,
'updated_at' => null,
'deleted_at' => null,
];
protected $casts = [
"conversacion_id" => "?int",
"usuario_id" => "?int",
"cliente_id" => "?int"
];
}

View File

@ -0,0 +1,5 @@
<?php
return [
"chat" => "Chat"
];

View File

@ -0,0 +1,101 @@
<?php
namespace App\Models\Chat;
use App\Models\Usuarios\UserModel;
use CodeIgniter\Model;
class ChatDeparmentModel extends Model
{
protected $table = 'chat_departments';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $protectFields = true;
protected $allowedFields = [
"name",
"display",
"description",
"type"
];
protected bool $allowEmptyInserts = false;
protected bool $updateOnlyChanged = true;
protected array $casts = [];
protected array $castHandlers = [];
// Dates
protected $useTimestamps = false;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
public function getChatDepartments(string $type = "general"): array
{
$userModel = model(UserModel::class);
$query = $this->db->table('chat_departments')
->select(
[
'chat_departments.id',
'chat_departments.name',
'chat_departments.display',
'chat_department_users.user_id'
]
)
->join(
"chat_department_users",
"chat_department_users.chat_department_id = chat_departments.id",
'left'
)
->join(
"users",
"chat_department_users.user_id = users.id",
'left'
)->where("chat_departments.type",$type)
->where("chat_department_users.user_id",auth()->user()->id);
$results = $query->get()->getResultArray();
// Create the desired structure
$departments = [];
foreach ($results as $row) {
$departmentName = $row['name'];
// If the department is not yet added to the array, initialize it
if (!isset($departments[$departmentName])) {
$departments[$departmentName] = [
'id' => $row['id'],
'name' => $row['name'],
'display' => $row['display'],
'users' => [] // Initialize users as an empty array
];
}
// If user_id is not null, add the user to the department's 'users' array
if ($row['user_id']) {
$departments[$departmentName]['users'][] = $userModel->find($row["user_id"]);
}
}
return $departments;
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Models\Chat;
use CodeIgniter\Model;
class ChatDeparmentUserModel extends Model
{
protected $table = 'chat_department_users';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $protectFields = true;
protected $allowedFields = [
"chat_department_id",
"user_id"
];
protected bool $allowEmptyInserts = false;
protected bool $updateOnlyChanged = true;
protected array $casts = [];
protected array $castHandlers = [];
// Dates
protected $useTimestamps = false;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
}

View File

@ -0,0 +1,121 @@
<?php
namespace App\Models\Chat;
use App\Models\Usuarios\UserModel;
use CodeIgniter\Model;
class ChatMessageModel extends Model
{
protected $table = 'chat_messages';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $protectFields = true;
protected $allowedFields = [
"message",
"chat_id",
"sender_id",
"receiver_id",
"viewed"
];
protected bool $allowEmptyInserts = false;
protected bool $updateOnlyChanged = true;
protected array $casts = [];
protected array $castHandlers = [];
// Dates
protected $useTimestamps = false;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
/**
* Devuelve los mensajes del chat
*
* @param integer $chat_id
* @return object
*/
public function get_chat_messages(int $chat_id): array
{
$user = model(UserModel::class);
$messages = $this->builder()->where("chat_id", $chat_id)->orderBy("created_at", "asc")->get()->getResultObject();
foreach ($messages as $message) {
$message->pos = auth()->user()->id == $message->sender_id ? "right" : "left";
if (auth()->user()->id == $message->sender_id) {
$message->user = auth()->user();
} else {
$message->user = $user->find($message->sender_id);
}
}
return $messages;
}
public function get_chat_contact_messages(int $receiver_id): array
{
$conversationArray = [];
$userModel = model(UserModel::class);
$receiverUser = $userModel->find($receiver_id);
$messagesFromClient = $this->builder()
->where("sender_id", auth()->user()->id)
->where("receiver_id", $receiverUser->id)
->get()->getResultObject();
$messagesFromReceiver = $this->builder()
->where("sender_id", $receiver_id)
->where("receiver_id", auth()->user()->id)
->get()->getResultObject();
foreach ($messagesFromClient as $message) {
$message->pos = "right";
$message->user = auth()->user();
$conversationArray[] = $message;
}
foreach ($messagesFromReceiver as $message) {
$message->pos = "left";
$message->user = $receiverUser;
$conversationArray[] = $message;
}
$dates = array();
foreach ($conversationArray as $key => $row) {
$dates[$key] = strtotime($row->created_at);
}
array_multisort($dates, SORT_ASC, $conversationArray);
return $conversationArray;
}
public function get_chat_unread_messages_count(int $sender_id): int
{
$messagesFromReceiver = $this->builder()
->where("sender_id", $sender_id)
->where("viewed", false)
->where("receiver_id", auth()->user()->id)->countAllResults();
return $messagesFromReceiver;
}
public function set_chat_messages_as_read(int $sender_id): int
{
$messagesFromReceiver = $this->builder()
->set("viewed", true)
->where("sender_id", $sender_id)
->where("receiver_id", auth()->user()->id)->update();
return $messagesFromReceiver;
}
}

View File

@ -0,0 +1,196 @@
<?php
namespace App\Models\Chat;
use CodeIgniter\Model;
use stdClass;
class ChatModel extends Model
{
protected $table = 'chats';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $protectFields = true;
protected $allowedFields = [
"pedido_id",
"chat_department_id",
"presupuesto_id",
"factura_id",
];
protected bool $allowEmptyInserts = false;
protected bool $updateOnlyChanged = true;
protected array $casts = [];
protected array $castHandlers = [];
// Dates
protected $useTimestamps = false;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
public function getChat(int $chat_id): array
{
return $this->db->table('chats')
->select(
[
"chats.*",
"chat_messages.created_at as messageCreatedAt",
"chat_messages.message as messageText",
]
)
->join("chat_messages", "chats.id = chat_messages.chat_id", "left")
->orderBy("created_at", "desc")
->where("chats.id", $chat_id)
->get()->getResultObject();
}
public function getChatPresupuesto(int $chat_department_id, int $presupuesto_id)
{
return $this->builder()->where("presupuesto_id", $presupuesto_id)->where("chat_department_id", $chat_department_id)->get()->getFirstRow();
}
public function getChatPedido(int $chat_department_id, int $pedido_id)
{
return $this->builder()->where("pedido_id", $pedido_id)->where("chat_department_id", $chat_department_id)->get()->getFirstRow();
}
public function getChatFactura(int $chat_department_id, int $factura_id)
{
return $this->builder()->where("factura_id", $factura_id)->where("chat_department_id", $chat_department_id)->get()->getFirstRow();
}
public function createChatPresupuesto(int $chat_department_id, int $presupuesto_id): int
{
return $this->insert([
"presupuesto_id" => $presupuesto_id,
"chat_department_id" => $chat_department_id
]);
}
public function createChatPedido(int $chat_department_id, int $pedido_id) : int
{
return $this->insert([
"pedido_id" => $pedido_id,
"chat_department_id" => $chat_department_id
]);
}
public function createChatFactura(int $chat_department_id, int $factura_id) : int
{
return $this->insert([
"factura_id" => $factura_id,
"chat_department_id" => $chat_department_id
]);
}
public function createChatSingle() : int
{
return $this->insert(["chat_department_id" => null]);
}
public function existChatPresupuesto(int $chat_department_id, int $presupuesto_id): bool
{
$countChatPresupuesto = $this->builder()
->where("presupuesto_id", $presupuesto_id)
->where("chat_department_id", $chat_department_id)
->countAllResults();
return $countChatPresupuesto > 0;
}
public function existChatPedido(int $chat_department_id, int $pedido_id): bool
{
$countChatPresupuesto = $this->builder()
->where("pedido_id", $pedido_id)
->where("chat_department_id", $chat_department_id)
->countAllResults();
return $countChatPresupuesto > 0;
}
public function existChatFactura(int $chat_department_id, int $factura_id): bool
{
$countChatPresupuesto = $this->builder()
->where("factura_id", $factura_id)
->where("chat_department_id", $chat_department_id)
->countAllResults();
return $countChatPresupuesto > 0;
}
public function getChatPedidosChat() : array
{
$query = $this->db->table("chats")
->select([
"chats.id as chatId",
"chats.pedido_id as pedidoId",
"chats.chat_department_id as chatDepartmentId",
"chat_departments.display as chatDisplay",
"pedidos.id as title"
])
->join("chat_departments","chat_departments.id = chats.chat_department_id","left")
->join("pedidos","pedidos.id = chats.pedido_id","left")
->get()->getResultObject();
return $query;
}
public function getChatPresupuestosChat() : array
{
$query = $this->db->table("chats")
->select([
"chats.id as chatId",
"chats.pedido_id as pedidoId",
"chats.chat_department_id as chatDepartmentId",
"chat_departments.display as chatDisplay",
"presupuestos.titulo as title"
])
->join("chat_departments","chat_departments.id = chats.chat_department_id","left")
->join("presupuestos","presupuestos.id = chats.pedido_id","left")
->get()->getResultObject();
return $query;
}
public function getChatFacturasChat() : array
{
$query = $this->db->table("chats")
->select([
"chats.id as chatId",
"chats.pedido_id as pedidoId",
"chats.chat_department_id as chatDepartmentId",
"chat_departments.display as chatDisplay",
"facturas.numero as title"
])
->join("chat_departments","chat_departments.id = chats.chat_department_id","left")
->join("facturas","facturas.id = chats.pedido_id","left")
->get()->getResultObject();
return $query;
}
public function getChatSingleChat() : array
{
$query = $this->db->table("chats")
->select([
"chats.id as chatId",
"chats.chat_department_id as chatDepartmentId",
"chat_departments.display as chatDisplay",
"facturas.numero as title"
])
->join("chat_departments","chat_departments.id = chats.chat_department_id","left")
->join("facturas","facturas.id = chats.pedido_id","left")
->get()->getResultObject();
return $query;
}
}

View File

@ -0,0 +1,201 @@
<?php
namespace App\Models\Mensajeria;
use CodeIgniter\Model;
class ConversacionModel extends Model
{
protected $table = 'chat_conversaciones'; // Asegúrate de que este sea el nombre correcto de la tabla
protected $primaryKey = 'id';
protected $returnType = 'App\Entities\Mensajeria\ConversacionEntity';
protected $useTimestamps = true;
protected $useSoftDeletes = true;
protected $allowedFields = [
'asunto',
'pedido_libro_id',
'pedido_maquetacion_id',
'factura_id',
'departamento'
];
public function pedidoLibro()
{
return $this->db
->table('pedido_libros')
->where('id', $this->pedido_libro_id)
->get()
->getFirstRow();
}
public function pedidoMaquetacion()
{
return $this->db
->table('pedido_maquetaciones')
->where('id', $this->pedido_maquetacion_id)
->get()
->getFirstRow();
}
public function factura()
{
return $this->db
->table('facturas')
->where('id', $this->factura_id)
->get()
->getFirstRow();
}
public function getConversacion($convesacionId)
{
$builder = $this->db
->table($this->table . " t1")
//->select("t1.conversacion_id AS id, t2.asunto AS asunto")
->where("t1.id", $convesacionId)
->join("chat_participantes t2", "t2.conversacion_id = t1.id", "left")
->join("chat_mensajes t3", "t3.conversacion_id = t1.id", "left")
->orderBy('t1.created_at', 'DESC')
->get()
->getResultArray();
return $builder;
}
public function getChatParticipants($chatId)
{
return $this->db
->table($this->table . " t1")
->select("t2.usuario_id as user_id, t3.first_name AS nombre, t3.last_name AS apellidos")
->where("t1.id", $chatId)
->join("chat_participantes t2", "t2.conversacion_id = t1.id", "left")
->join("users t3", "t3.id = t2.usuario_id", "left")
->get()
->getResultArray();
}
public function getChatMessages($chatId)
{
return $this->db
->table($this->table . " t1")
->select("t2.mensaje AS mensaje, t2.usuario_id as user_id")
->select("t3.first_name AS nombre, t3.last_name AS apellidos")
->where("t1.id", intval($chatId))
->join("chat_mensajes t2", "t2.conversacion_id = t1.id", "left")
->join("users t3", "t3.id = t2.usuario_id", "left")
->get()
->getResultArray();
}
/**
* Devuelve si la conversacion tiene algún cliente
*/
public function isClient()
{
$isCliente = false;
$participantes = $this->participantes(); // Asegúrate de implementar este método
foreach ($participantes as $p) {
if (($p->user && $p->user->customer_id) || $p->customer_id) {
$isCliente = true;
break;
}
}
return $isCliente;
}
/**
* Comprueba si la conversacion del mensaje tiene el cliente
*/
public function haveCliente($userCustomer)
{
$customer_id = $userCustomer->customer_id;
$participants = $this->participantes(); // Asegúrate de implementar este método
foreach ($participants as $p) {
if ($p->customer_id && $p->customer_id === $customer_id) {
return true;
}
}
return false;
}
public function getParticipanteDesdeCliente($cliente_id)
{
return $this->db->table('chat_participantes')
->where('cliente_id', $cliente_id)
->where('conversacion_id', $this->id)
->get()
->getFirstRow();
}
public function marcarLeidoCliente($cliente_id)
{
try {
$participante = $this->getParticipanteDesdeCliente($cliente_id);
if ($participante) {
$this->db->table('chat_participantes')
->where('id', $participante->id)
->update(['last_read' => date('Y-m-d H:i:s')]);
}
} catch (\Exception $e) {
// do nothing
}
}
public function marcarNoLeido($usuario_id)
{
try {
$participante = $this->getParticipanteDesdeUsuario($usuario_id); // Asegúrate de implementar este método
if ($participante) {
$this->db->table('chat_participantes')
->where('id', $participante->id)
->update(['last_read' => null]);
}
} catch (\Exception $e) {
// do nothing
}
}
protected function participantes()
{
return $this->db
->table('chat_participantes')
->where('conversacion_id', $this->id)
->get()
->getResult();
}
protected function getParticipanteDesdeUsuario($usuario_id)
{
return $this->db->table('chat_participantes')
->where('usuario_id', $usuario_id)
->where('conversacion_id', $this->id)
->get()
->getFirstRow();
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace App\Models\Mensajeria;
use App\Models\BaseModel;
use App\Models\Customers\Customer;
class ParticipanteModel extends BaseModel
{
protected $table = 'chat_participantes';
protected $primaryKey = 'id';
protected $returnType = 'App\Entities\Mensajeria\ParticipanteEntity';
protected $useTimestamps = true;
protected $useSoftDeletes = true;
protected $allowedFields = [
'conversacion_id',
'usuario_id',
'cliente_id',
'email',
'last_read',
];
public function getPeopleInChat($conversacionId){
$builder = $this->db
->table($this->table . " t1")
->where("t1.conversacion_id", $conversacionId);
return $builder;
}
public function getChatsByUser($userId){
$builder = $this->db
->table($this->table . " t1")
->select("t1.conversacion_id AS id, t2.asunto AS asunto")
->where("t1.usuario_id", $userId)
->join("chat_conversaciones t2", "t2.id = conversacion_id", "left")
->orderBy('t1.created_at', 'DESC')
->get()
->getResultObject();
return $builder;
}
public function getCustomer()
{
//$customerModel = new Customer();
//return $customerModel->find($this->attributes['customer_id']);
}
}

View File

@ -781,4 +781,7 @@ class PresupuestoModel extends \App\Models\BaseModel
return $description_interior . $description_cubierta . $description_sobrecubierta . $acabado;
}
}

View File

@ -0,0 +1,151 @@
<div class="accordion accordion-bordered mt-3" id="accordionChatFactura">
<div class="card accordion-item active">
<h2 class="accordion-header" id="headingChatFactura">
<button type="button" class="accordion-button" data-bs-toggle="collapse"
data-bs-target="#accordionChatFacturaTip" aria-expanded="false" aria-controls="accordionChatFacturaTip">
<h3><?= lang("Chat.chat") ?></h3>
</button>
</h2>
<div id="accordionChatFacturaTip" class="accordion-collapse collapse show"
data-bs-parent="#accordionChatFactura">
<div class="accordion-body">
<div class="container-xxl flex-grow-1 container-p-y" id="chat-factura" data-id="<?= $modelId ?>">
<div class="app-chat card overflow-hidden">
<div class="row g-0">
<!-- Chat & Contacts -->
<div class="col app-chat-contacts app-sidebar flex-grow-0 overflow-hidden border-end"
id="app-chat-contacts">
<div class="sidebar-header">
<div class="d-flex align-items-center me-3 me-lg-0">
<div class="flex-shrink-0 avatar me-3">
<span class="avatar-initial rounded-circle bg-label-success">U</span>
</div>
<div class="flex-grow-1 input-group input-group-merge rounded-pill">
<span class="input-group-text" id="basic-addon-search31">
<i class="ti ti-search"></i>
</span>
<input type="text" class="form-control chat-search-input"
placeholder="Buscar..." aria-label="Buscar..."
aria-describedby="basic-addon-search31" />
</div>
</div>
<i class="ti ti-x cursor-pointer d-lg-none d-block position-absolute mt-2 me-1 top-0 end-0"
data-overlay data-bs-toggle="sidebar" data-target="#app-chat-contacts"></i>
</div>
<hr class="container-m-nx m-0" />
<div class="sidebar-body">
<div class="chat-contact-list-item-title">
<h5 class="text-primary mb-0 px-4 pt-3 pb-2">Departamentos</h5>
</div>
<!-- Chats -->
<ul class="list-unstyled chat-contact-list" id="chat-list">
<li class="chat-contact-list-item chat-list-item-0 d-none">
<h6 class="text-muted mb-0">No Chats Found</h6>
</li>
<!-- CHAT LIST -->
</ul>
<!-- Contacts -->
<ul class="list-unstyled chat-contact-list mb-0" id="contact-list">
<li class="chat-contact-list-item chat-contact-list-item-title">
<h5 class="text-primary mb-0">Contacts</h5>
</li>
<li class="chat-contact-list-item contact-list-item-0 d-none">
<h6 class="text-muted mb-0">No Contacts Found</h6>
</li>
</ul>
</div>
</div>
<!-- /Chat contacts -->
<!-- Chat History -->
<div class="col app-chat-history bg-body">
<div class="chat-history-wrapper ">
<div class="chat-history-header border-bottom">
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex overflow-hidden align-items-center">
<i class="ti ti-menu-2 ti-sm cursor-pointer d-lg-none d-block me-2"
data-bs-toggle="sidebar" data-overlay
data-target="#app-chat-contacts"></i>
<div class="avatar d-block flex-shrink-0">
<span
class="avatar-initial rounded-circle bg-label-primary">P</span>
</div>
<div class="chat-contact-info flex-grow-1 ms-2">
<h6 class="m-0">Departamento Producción</h6>
<small class="user-status text-muted">Consulta sobre el presupuesto
P001</small>
</div>
</div>
<div class="d-flex align-items-center">
<div class="dropdown d-flex align-self-center">
<button class="btn p-0" type="button" id="chat-header-actions"
data-bs-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
<i class="ti ti-dots-vertical"></i>
</button>
<div class="dropdown-menu dropdown-menu-end d-none"
aria-labelledby="chat-header-actions">
<a class="dropdown-item" href="javascript:void(0);">Silenciar
Conversacion</a>
<a class="dropdown-item" href="javascript:void(0);">Limpiar
Conversacion</a>
</div>
</div>
</div>
</div>
</div>
<div class="chat-history-body bg-body ">
<ul class="list-unstyled chat-history" id="chat-conversation">
</ul>
</div>
<!-- Chat message form -->
<div class="chat-history-footer shadow-sm">
<div
class="form-send-message d-flex justify-content-between align-items-center">
<input class="form-control message-input border-0 me-3 shadow-none"
placeholder="Type your message here" />
<div class="message-actions d-flex align-items-center">
<a class="btn btn-primary d-flex send-msg-btn" style="color:white"
id="send-msg-btn-deparment">
<i class="ti ti-send me-md-1 me-0"></i>
<span class="align-middle d-md-inline-block">Enviar</span>
</a>
<a class="btn btn-primary d-flex send-msg-btn"
id="send-msg-btn-internal">
<i class="ti ti-send me-md-1 me-0"></i>
<span class="align-middle d-md-inline-block ">Enviar</span>
</a>
</div>
</div>
</div>
</div>
</div>
<!-- /Chat History -->
<div class="app-overlay"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<?= $this->section('css') ?>
<link rel="stylesheet" href="<?= site_url('themes/vuexy/vendor/css/pages/app-chat.css') ?>">
<?= $this->endSection() ?>
<!------------------------------------------------------->
<!-- Código JS logica -->
<!------------------------------------------------------->
<?= $this->section("additionalExternalJs") ?>
<script type="module" src="<?= site_url('assets/js/safekat/pages/chatFactura.js') ?>"></script>
<script src="<?= site_url('themes/vuexy/vendor/libs/perfect-scrollbar/perfect-scrollbar.js') ?>"></script>
<!-- <script src="<?= site_url('js_loader/chat_js') ?>"></script> -->
<?= $this->endSection() ?>

View File

@ -0,0 +1,152 @@
<div class="accordion accordion-bordered mt-3" id="accordionChatGeneral">
<div class="card accordion-item active">
<h2 class="accordion-header" id="headingChatGeneral">
<button type="button" class="accordion-button" data-bs-toggle="collapse"
data-bs-target="#accordionChatGeneralTip" aria-expanded="false" aria-controls="accordionChatGeneralTip">
<h3><?= lang("Chat.chat") ?></h3>
</button>
</h2>
<div id="accordionChatGeneralTip" class="accordion-collapse collapse show"
data-bs-parent="#accordionChatGeneral">
<div class="accordion-body">
<div class="container-xxl flex-grow-1 container-p-y" id="chat-general" data-id="<?= null ?>">
<div class="app-chat card overflow-hidden">
<div class="row g-0">
<!-- Chat & Contacts -->
<div class="col app-chat-contacts app-sidebar flex-grow-0 overflow-hidden border-end"
id="app-chat-contacts">
<div class="sidebar-header">
<div class="d-flex align-items-center me-3 me-lg-0">
<div class="flex-shrink-0 avatar me-3">
<span class="avatar-initial rounded-circle bg-label-success">U</span>
</div>
<div class="flex-grow-1 input-group input-group-merge rounded-pill">
<span class="input-group-text" id="basic-addon-search31">
<i class="ti ti-search"></i>
</span>
<input type="text" class="form-control chat-search-input"
placeholder="Buscar..." aria-label="Buscar..."
aria-describedby="basic-addon-search31" />
</div>
</div>
<i class="ti ti-x cursor-pointer d-lg-none d-block position-absolute mt-2 me-1 top-0 end-0"
data-overlay data-bs-toggle="sidebar" data-target="#app-chat-contacts"></i>
</div>
<hr class="container-m-nx m-0" />
<div class="sidebar-body">
<!-- Contacts -->
<ul class="list-unstyled chat-contact-list mb-0" id="contact-list">
<li class="chat-contact-list-item chat-contact-list-item-title">
<h5 class="text-primary mb-0">Contactos</h5>
</li>
<li class="chat-contact-list-item contact-list-item-0 ">
<h6 class="text-muted mb-0">No Contacts Found</h6>
</li>
<!-- <li class="chat-contact-list-item">
<a class="d-flex align-items-center">
<div class="avatar d-block flex-shrink-0">
<span class="avatar-initial rounded-circle bg-label-primary">JJ</span>
</div>
<div class="chat-contact-info flex-grow-1 ms-2">
<h6 class="chat-contact-name text-truncate m-0">Jaime Jimenez</h6>
</div>
</a>
</li> -->
</ul>
</div>
</div>
<!-- /Chat contacts -->
<!-- Chat History -->
<div class="col app-chat-history bg-body">
<div class="chat-history-wrapper ">
<div class="chat-history-header border-bottom">
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex overflow-hidden align-items-center">
<i class="ti ti-menu-2 ti-sm cursor-pointer d-lg-none d-block me-2"
data-bs-toggle="sidebar" data-overlay
data-target="#app-chat-contacts"></i>
<div class="avatar d-block flex-shrink-0">
<span
class="avatar-initial rounded-circle bg-label-primary">P</span>
</div>
<div class="chat-contact-info flex-grow-1 ms-2">
<h6 class="m-0">Departamento Producción</h6>
<small class="user-status text-muted">Consulta sobre el presupuesto
P001</small>
</div>
</div>
<div class="d-flex align-items-center">
<div class="dropdown d-flex align-self-center">
<button class="btn p-0" type="button" id="chat-header-actions"
data-bs-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
<i class="ti ti-dots-vertical"></i>
</button>
<div class="dropdown-menu dropdown-menu-end"
aria-labelledby="chat-header-actions">
<a class="dropdown-item" href="javascript:void(0);">Silenciar
Conversacion</a>
<a class="dropdown-item" href="javascript:void(0);">Limpiar
Conversacion</a>
</div>
</div>
</div>
</div>
</div>
<div class="chat-history-body bg-body ">
<ul class="list-unstyled chat-history" id="chat-conversation">
</ul>
</div>
<!-- Chat message form -->
<div class="chat-history-footer shadow-sm">
<div
class="form-send-message d-flex justify-content-between align-items-center">
<input class="form-control message-input border-0 me-3 shadow-none"
placeholder="Type your message here" />
<div class="message-actions d-flex align-items-center">
<div class="message-actions d-flex align-items-center">
<a class="btn btn-primary d-flex send-msg-btn" style="color:white"
id="send-msg-btn-deparment">
<i class="ti ti-send me-md-1 me-0"></i>
<span class="align-middle d-md-inline-block">Enviar</span>
</a>
<a class="btn btn-primary d-flex send-msg-btn"
id="send-msg-btn-internal">
<i class="ti ti-send me-md-1 me-0"></i>
<span class="align-middle d-md-inline-block ">Enviar</span>
</a>
</div>
</div>
</div>
</div>
</div>
<!-- /Chat History -->
<div class="app-overlay"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<?= $this->section('css') ?>
<link rel="stylesheet" href="<?= site_url('themes/vuexy/vendor/css/pages/app-chat.css') ?>">
<?= $this->endSection() ?>
<!------------------------------------------------------->
<!-- Código JS logica -->
<!------------------------------------------------------->
<?= $this->section("additionalExternalJs") ?>
<script type="module" src="<?= site_url('assets/js/safekat/pages/chatGeneral.js') ?>"></script>
<script src="<?= site_url('themes/vuexy/vendor/libs/perfect-scrollbar/perfect-scrollbar.js') ?>"></script>
<!-- <script src="<?= site_url('js_loader/chat_js') ?>"></script> -->
<?= $this->endSection() ?>

View File

@ -0,0 +1,152 @@
<div class="accordion accordion-bordered mt-3" id="accordionChatPedido">
<div class="card accordion-item active">
<h2 class="accordion-header" id="headingChatPedido">
<button type="button" class="accordion-button" data-bs-toggle="collapse"
data-bs-target="#accordionChatPedidoTip" aria-expanded="false" aria-controls="accordionChatPedidoTip">
<h3><?= lang("Chat.chat") ?></h3>
</button>
</h2>
<div id="accordionChatPedidoTip" class="accordion-collapse collapse show" data-bs-parent="#accordionChatPedido">
<div class="accordion-body">
<div class="container-xxl flex-grow-1 container-p-y" id="chat-pedido" data-id="<?= $modelId ?>">
<div class="app-chat card overflow-hidden">
<div class="row g-0">
<!-- Chat & Contacts -->
<div class="col app-chat-contacts app-sidebar flex-grow-0 overflow-hidden border-end"
id="app-chat-contacts">
<div class="sidebar-header">
<div class="d-flex align-items-center me-3 me-lg-0">
<div class="flex-shrink-0 avatar me-3">
<span class="avatar-initial rounded-circle bg-label-success">U</span>
</div>
<div class="flex-grow-1 input-group input-group-merge rounded-pill">
<span class="input-group-text" id="basic-addon-search31">
<i class="ti ti-search"></i>
</span>
<input type="text" class="form-control chat-search-input"
placeholder="Buscar..." aria-label="Buscar..."
aria-describedby="basic-addon-search31" />
</div>
</div>
<i class="ti ti-x cursor-pointer d-lg-none d-block position-absolute mt-2 me-1 top-0 end-0"
data-overlay data-bs-toggle="sidebar" data-target="#app-chat-contacts"></i>
</div>
<hr class="container-m-nx m-0" />
<div class="sidebar-body">
<div class="chat-contact-list-item-title">
<h5 class="text-primary mb-0 px-4 pt-3 pb-2">Departamentos</h5>
</div>
<!-- Chats -->
<ul class="list-unstyled chat-contact-list" id="chat-list">
<li class="chat-contact-list-item chat-list-item-0 d-none">
<h6 class="text-muted mb-0">No Chats Found</h6>
</li>
<!-- CHAT LIST -->
</ul>
<!-- Contacts -->
<ul class="list-unstyled chat-contact-list mb-0" id="contact-list">
<li class="chat-contact-list-item chat-contact-list-item-title">
<h5 class="text-primary mb-0">Contactos</h5>
</li>
<li class="chat-contact-list-item contact-list-item-0 d-none">
<h6 class="text-muted mb-0">No Contacts Found</h6>
</li>
</ul>
</div>
</div>
<!-- /Chat contacts -->
<!-- Chat History -->
<div class="col app-chat-history bg-body">
<div class="chat-history-wrapper ">
<div class="chat-history-header border-bottom">
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex overflow-hidden align-items-center">
<i class="ti ti-menu-2 ti-sm cursor-pointer d-lg-none d-block me-2"
data-bs-toggle="sidebar" data-overlay
data-target="#app-chat-contacts"></i>
<div class="avatar d-block flex-shrink-0">
<span
class="avatar-initial rounded-circle bg-label-primary">P</span>
</div>
<div class="chat-contact-info flex-grow-1 ms-2">
<h6 class="m-0"></h6>
<small class="user-status text-muted"></small>
</div>
</div>
<div class="d-flex align-items-center">
<div class="dropdown d-flex align-self-center">
<button class="btn p-0" type="button" id="chat-header-actions"
data-bs-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
<i class="ti ti-dots-vertical"></i>
</button>
<div class="dropdown-menu dropdown-menu-end"
aria-labelledby="chat-header-actions">
<a class="dropdown-item" href="javascript:void(0);">Silenciar
Conversacion</a>
<a class="dropdown-item" href="javascript:void(0);">Limpiar
Conversacion</a>
</div>
</div>
</div>
</div>
</div>
<div class="chat-history-body bg-body ">
<ul class="list-unstyled chat-history" id="chat-conversation">
</ul>
</div>
<!-- Chat message form -->
<div class="chat-history-footer shadow-sm">
<div
class="form-send-message d-flex justify-content-between align-items-center">
<input class="form-control message-input border-0 me-3 shadow-none"
placeholder="Type your message here" />
<div class="message-actions d-flex align-items-center">
<a class="btn btn-primary d-flex send-msg-btn" style="color:white"
id="send-msg-btn-deparment">
<i class="ti ti-send me-md-1 me-0"></i>
<span class="align-middle d-md-inline-block">Enviar</span>
</a>
<a class="btn btn-primary d-flex send-msg-btn"
id="send-msg-btn-internal">
<i class="ti ti-send me-md-1 me-0"></i>
<span class="align-middle d-md-inline-block ">Enviar</span>
</a>
</div>
</div>
</div>
</div>
</div>
<!-- /Chat History -->
<div class="app-overlay"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<?= $this->section('css') ?>
<link rel="stylesheet" href="<?= site_url('themes/vuexy/vendor/css/pages/app-chat.css') ?>">
<?= $this->endSection() ?>
<!------------------------------------------------------->
<!-- Código JS logica -->
<!------------------------------------------------------->
<?= $this->section("additionalExternalJs") ?>
<script type="module" src="<?= site_url('assets/js/safekat/pages/chatPedido.js') ?>"></script>
<script src="<?= site_url('themes/vuexy/vendor/libs/perfect-scrollbar/perfect-scrollbar.js') ?>"></script>
<!-- <script src="<?= site_url('js_loader/chat_js') ?>"></script> -->
<?= $this->endSection() ?>

View File

@ -0,0 +1,153 @@
<div class="accordion accordion-bordered mt-3" id="accordionChatPresupuesto">
<div class="card accordion-item active">
<h2 class="accordion-header" id="headingChatPresupuesto">
<button type="button" class="accordion-button" data-bs-toggle="collapse"
data-bs-target="#accordionChatPresupuestoTip" aria-expanded="false"
aria-controls="accordionChatPresupuestoTip">
<h3><?= lang("Chat.chat") ?></h3>
</button>
</h2>
<div id="accordionChatPresupuestoTip" class="accordion-collapse collapse show"
data-bs-parent="#accordionChatPresupuesto">
<div class="accordion-body">
<div class="container-xxl flex-grow-1" id="chat-presupuesto" data-id="<?= $modelId ?>">
<div class="app-chat card overflow-hidden">
<div class="row g-0">
<!-- Chat & Contacts -->
<div class="col app-chat-contacts app-sidebar flex-grow-0 overflow-hidden border-end"
id="app-chat-contacts">
<div class="sidebar-header">
<div class="d-flex align-items-center me-3 me-lg-0">
<div class="flex-shrink-0 avatar me-3">
<span class="avatar-initial rounded-circle bg-label-success">U</span>
</div>
<div class="flex-grow-1 input-group input-group-merge rounded-pill">
<span class="input-group-text" id="basic-addon-search31">
<i class="ti ti-search"></i>
</span>
<input type="text" class="form-control chat-search-input"
placeholder="Buscar..." aria-label="Buscar..."
aria-describedby="basic-addon-search31" />
</div>
</div>
<i class="ti ti-x cursor-pointer d-lg-none d-block position-absolute mt-2 me-1 top-0 end-0"
data-overlay data-bs-toggle="sidebar" data-target="#app-chat-contacts"></i>
</div>
<hr class="container-m-nx m-0" />
<div class="sidebar-body">
<div class="chat-contact-list-item-title">
<h5 class="text-primary mb-0 px-4 pt-3 pb-2">Departamentos</h5>
</div>
<!-- Chats -->
<ul class="list-unstyled chat-contact-list" id="chat-list">
<li class="chat-contact-list-item chat-list-item-0 d-none">
<h6 class="text-muted mb-0">No Chats Found</h6>
</li>
<!-- CHAT LIST -->
</ul>
<!-- Contacts -->
<ul class="list-unstyled chat-contact-list mb-0 d-none" id="contact-list">
<li class="chat-contact-list-item chat-contact-list-item-title">
<h5 class="text-primary mb-0">Contacts</h5>
</li>
<li class="chat-contact-list-item contact-list-item-0 d-none">
<h6 class="text-muted mb-0">No Contacts Found</h6>
</li>
</ul>
</div>
</div>
<!-- /Chat contacts -->
<!-- Chat History -->
<div class="col app-chat-history bg-body">
<div class="chat-history-wrapper ">
<div class="chat-history-header border-bottom">
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex overflow-hidden align-items-center">
<i class="ti ti-menu-2 ti-sm cursor-pointer d-lg-none d-block me-2"
data-bs-toggle="sidebar" data-overlay
data-target="#app-chat-contacts"></i>
<div class="avatar d-block flex-shrink-0">
<span
class="avatar-initial rounded-circle bg-label-primary">P</span>
</div>
<div class="chat-contact-info flex-grow-1 ms-2">
<h6 class="m-0">Departamento Producción</h6>
<small class="user-status text-muted">Consulta sobre el presupuesto
P001</small>
</div>
</div>
<div class="d-flex align-items-center">
<div class="dropdown d-flex align-self-center">
<button class="btn p-0" type="button" id="chat-header-actions"
data-bs-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
<i class="ti ti-dots-vertical"></i>
</button>
<div class="dropdown-menu dropdown-menu-end"
aria-labelledby="chat-header-actions">
<a class="dropdown-item" href="javascript:void(0);">Silenciar
Conversacion</a>
<a class="dropdown-item" href="javascript:void(0);">Limpiar
Conversacion</a>
</div>
</div>
</div>
</div>
</div>
<div class="chat-history-body bg-body ">
<ul class="list-unstyled chat-history" id="chat-conversation">
</ul>
</div>
<!-- Chat message form -->
<div class="chat-history-footer shadow-sm">
<div
class="form-send-message d-flex justify-content-between align-items-center">
<input class="form-control message-input border-0 me-3 shadow-none"
placeholder="Type your message here" />
<div class="message-actions d-flex align-items-center">
<a class="btn btn-primary d-flex send-msg-btn" style="color:white"
id="send-msg-btn-deparment">
<i class="ti ti-send me-md-1 me-0"></i>
<span class="align-middle d-md-inline-block">Enviar</span>
</a>
<a class="btn btn-primary d-flex send-msg-btn"
id="send-msg-btn-internal">
<i class="ti ti-send me-md-1 me-0"></i>
<span class="align-middle d-md-inline-block ">Enviar</span>
</a>
</div>
</div>
</div>
</div>
</div>
<!-- /Chat History -->
<div class="app-overlay"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<?= $this->section('css') ?>
<link rel="stylesheet" href="<?= site_url('themes/vuexy/vendor/css/pages/app-chat.css') ?>">
<?= $this->endSection() ?>
<!------------------------------------------------------->
<!-- Código JS logica -->
<!------------------------------------------------------->
<?= $this->section("additionalExternalJs") ?>
<script type="module" src="<?= site_url('assets/js/safekat/pages/chatPresupuesto.js') ?>"></script>
<script src="<?= site_url('themes/vuexy/vendor/libs/perfect-scrollbar/perfect-scrollbar.js') ?>"></script>
<!-- <script src="<?= site_url('js_loader/chat_js') ?>"></script> -->
<?= $this->endSection() ?>

View File

@ -28,6 +28,7 @@
<?php if($facturaEntity->estado !='borrador' && (strpos($facturaEntity->numero, "REC ") === 0) ) : ?>
<?= view("themes/vuexy/form/facturas/_rectificadaFacturasItems") ?>
<?php endif; ?>
<?= view("themes/vuexy/components/chat_factura",data:["modelId" => $facturaEntity->id]) ?>
<div class="pt-4">
<?php if($facturaEntity->estado =='borrador') : ?>

View File

@ -0,0 +1,138 @@
/**
* App Chat
*/
// Seleccionar elementos del DOM
const chatContactsBody = $('.app-chat-contacts .sidebar-body'),
chatContactListItems = $('.chat-contact-list-item:not(.chat-contact-list-item-title)'),
chatHistoryBody = $('.chat-history-body'),
chatSidebarLeftUserAbout = $('.chat-sidebar-left-user-about'),
messageInput = $('.message-input'),
searchInput = $('.chat-search-input'),
sendMsgBtn = $('.send-msg-btn'); // Seleccionar el botón de envío de mensaje
// Inicializar PerfectScrollbar
if (chatContactsBody.length) {
new PerfectScrollbar(chatContactsBody[0], {
wheelPropagation: false,
suppressScrollX: true
});
}
if (chatHistoryBody.length) {
new PerfectScrollbar(chatHistoryBody[0], {
wheelPropagation: false,
suppressScrollX: true
});
}
// Función para desplazar el scroll al final
function scrollToBottom() {
if (chatHistoryBody.length) {
chatHistoryBody.scrollTop(chatHistoryBody[0].scrollHeight);
}
}
scrollToBottom();
// Seleccionar chat o contacto
chatContactListItems.on('click', function () {
chatContactListItems.removeClass('active');
$(this).addClass('active');
});
// Filtrar chats
if (searchInput.length) {
searchInput.on('keyup', function () {
const searchValue = $(this).val().toLowerCase(),
chatListItem0 = $('.chat-list-item-0'),
contactListItem0 = $('.contact-list-item-0'),
searchChatListItems = $('#chat-list li:not(.chat-contact-list-item-title)'),
searchContactListItems = $('#contact-list li:not(.chat-contact-list-item-title)');
// Buscar en chats
const chatListItemsCount = searchChatContacts(searchChatListItems, searchValue);
// Mostrar u ocultar mensaje de "No se encontraron resultados" en chats
if (chatListItem0.length) {
chatListItem0.toggleClass('d-none', chatListItemsCount === 0);
}
// Buscar en contactos
const contactListItemsCount = searchChatContacts(searchContactListItems, searchValue);
// Mostrar u ocultar mensaje de "No se encontraron resultados" en contactos
if (contactListItem0.length) {
contactListItem0.toggleClass('d-none', contactListItemsCount === 0);
}
});
}
// Función para buscar en chats y contactos
function searchChatContacts(searchListItems, searchValue) {
let searchListItemsCount = 0;
searchListItems.each(function () {
const searchListItemText = $(this).text().toLowerCase();
const matchesSearch = searchListItemText.indexOf(searchValue) !== -1;
$(this).toggleClass('d-flex', matchesSearch);
$(this).toggleClass('d-none', !matchesSearch);
if (matchesSearch) {
searchListItemsCount++;
}
});
return searchListItemsCount;
}
// Enviar mensaje
if (sendMsgBtn.length) {
// sendMsgBtn.on('click', function (e) {
// e.preventDefault();
// if (messageInput.val()) {
// const renderMsg = $('<div>').addClass('chat-message-text mt-2').html(`<p class="mb-0">${messageInput.val()}</p>`);
// const lastChatMessageWrapper = $('li:last-child .chat-message-wrapper');
// if (lastChatMessageWrapper.length) {
// lastChatMessageWrapper.append(renderMsg);
// }
// messageInput.val('');
// scrollToBottom();
// }
// });
}
// // Seleccionar los elementos <p> con la clase .chat-contact-status
// $('.chat-contact-status').on('click', function () {
// // Obtener el id de la conversación desde el atributo id del elemento <p>
// var conversationId = $(this).attr('id');
// console.log(conversationId)
// // Realizar la llamada AJAX
// $.ajax({
// url: 'internos/chat/' + conversationId, // Cambia esta URL por la ruta correcta a tu API
// type: 'GET',
// dataType: 'json',
// success: function (data) {
// // Manejar la respuesta exitosa de la llamada AJAX
// console.log('Datos recibidos:', data);
// // Aquí puedes actualizar el DOM o realizar otras acciones con los datos recibidos
// if (Array.isArray(data.people) && data.people.length > 0) {
// // Limpiar el contenedor donde se mostrarán los participantes
// $('#participants-container').empty();
// data.people.forEach(person => {
// // Crear el HTML para cada participante y agregarlo al contenedor
// var participantHtml = `
// <div class="avatar d-block flex-shrink-0 me-2">
// <span class="avatar-initial rounded-circle bg-label-primary ">${person.user_id}</span>
// </div>`;
// $('#participants-container').append(participantHtml);
// });
// }
// },
// error: function (jqXHR, textStatus, errorThrown) {
// // Manejar errores en la llamada AJAX
// console.error('Error en la llamada AJAX:', textStatus, errorThrown);
// }
// });
// })
// ;

View File

@ -0,0 +1,10 @@
<?= $this->include("themes/_commonPartialsBs/select2bs5") ?>
<?= $this->include("themes/_commonPartialsBs/datatables") ?>
<?= $this->extend('themes/vuexy/main/defaultlayout') ?>
<?= $this->section('content'); ?>
<!--Content Body-->
<div class="container-xxl flex-grow-1 container-p-y">
<?= view("themes/vuexy/components/chat_general", data: ["modelId" => null]) ?>
</div>
<?= $this->endSection() ?>

View File

@ -1,53 +1,62 @@
<?= $this->include("themes/_commonPartialsBs/datatables") ?>
<?= $this->include("themes/_commonPartialsBs/select2bs5") ?>
<?= $this->include("themes/_commonPartialsBs/sweetalert") ?>
<?=$this->extend('themes/vuexy/main/defaultlayout') ?>
<?= $this->extend('themes/vuexy/main/defaultlayout') ?>
<?= $this->section("content") ?>
<div class="row">
<div class="col-12">
<div class="card card-info">
<div class="card-header">
<h3 class="card-title"><?= $boxTitle ?? $pageTitle ?></h3>
</div><!--//.card-header -->
<form id="pedidoForm" method="post" class="card-body" action="<?= $formAction ?>">
<?= csrf_field() ?>
<div class="card card-info">
<div class="card-header">
<h3 class="card-title"><?= $boxTitle ?? $pageTitle ?></h3>
</div>
<!--//.card-header -->
<form id="pedidoForm" method="post" class="card-body" action="<?= $formAction ?>">
<?= csrf_field() ?>
<div class="card-body">
<?= view("themes/_commonPartialsBs/_alertBoxes") ?>
<?= !empty($validation->getErrors()) ? $validation->listErrors("bootstrap_style") : "" ?>
<?= view("themes/vuexy/form/pedidos/_cabeceraItems") ?>
<?= view("themes/vuexy/form/pedidos/_lineasItems") ?>
<?php if (!(auth()->user()->inGroup('cliente-admin') || auth()->user()->inGroup('cliente-editor'))) : ?>
<?= view("themes/vuexy/form/pedidos/_albaranesItems") ?>
<?= view("themes/vuexy/form/pedidos/_albaranesItems") ?>
<?php endif; ?>
<?= view("themes/vuexy/form/pedidos/_facturasItems") ?>
<?= view("themes/vuexy/components/chat_pedido", data: ["modelId" => $pedidoEntity->id]) ?>
</div><!-- /.card-body -->
<div class="pt-4">
<?= anchor(route_to("listaPresupuestos"), lang("Basic.global.Cancel"), ["class" => "btn btn-secondary float-start"]) ?>
</div><!-- /.card-footer -->
</form>
</form>
</div><!-- //.card -->
</div><!--//.col -->
</div><!--//.row -->
</div>
<!--//.col -->
</div>
<!--//.row -->
<?= view("themes/_commonPartialsBs/_modalConfirmDialog") ?>
<?= $this->endSection() ?>
<?=$this->section('css') ?>
<link rel="stylesheet" href="<?= site_url("/themes/vuexy/vendor/libs/flatpickr/flatpickr.css") ?>">
<link rel="stylesheet" href="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/buttons.bootstrap5.min.css") ?>">
<link rel="stylesheet" href="<?= site_url('themes/vuexy/css/sk-datatables.css') ?>">
<?=$this->endSection() ?>
<?= $this->section('css') ?>
<link rel="stylesheet" href="<?= site_url("/themes/vuexy/vendor/libs/flatpickr/flatpickr.css") ?>">
<link rel="stylesheet"
href="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/buttons.bootstrap5.min.css") ?>">
<link rel="stylesheet" href="<?= site_url('themes/vuexy/css/sk-datatables.css') ?>">
<?= $this->endSection() ?>
<?= $this->section('additionalExternalJs') ?>
<script src="<?= site_url("/themes/vuexy/vendor/libs/flatpickr/flatpickr.js") ?>"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/dataTables.buttons.min.js") ?>"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/buttons.bootstrap5.min.js") ?>"></script>
<script src="<?= site_url("themes/vuexy/vendor/libs/datatables-sk/plugins/select/dataTables.select.min.js") ?>"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/buttons.html5.min.js") ?>"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/buttons.print.min.js") ?>"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/jszip/jszip.min.js") ?>"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/pdfmake/pdfmake.min.js") ?>" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/pdfmake/vfs_fonts.js") ?>"></script>
<?=$this->endSection() ?>
<script src="<?= site_url("/themes/vuexy/vendor/libs/flatpickr/flatpickr.js") ?>"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/dataTables.buttons.min.js") ?>">
</script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/buttons.bootstrap5.min.js") ?>">
</script>
<script src="<?= site_url("themes/vuexy/vendor/libs/datatables-sk/plugins/select/dataTables.select.min.js") ?>">
</script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/buttons.html5.min.js") ?>"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/buttons.print.min.js") ?>"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/jszip/jszip.min.js") ?>"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/pdfmake/pdfmake.min.js") ?>"
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/pdfmake/vfs_fonts.js") ?>"></script>
<?= $this->endSection() ?>

View File

@ -0,0 +1,152 @@
<div class="container-xxl flex-grow-1 container-p-y" id="chat-presupuesto" data-id="<?=$presupuestoId ?>">
<div class="app-chat card overflow-hidden">
<div class="row g-0">
<!-- Chat & Contacts -->
<div
class="col app-chat-contacts app-sidebar flex-grow-0 overflow-hidden border-end"
id="app-chat-contacts">
<div class="sidebar-header">
<div class="d-flex align-items-center me-3 me-lg-0">
<div class="flex-shrink-0 avatar me-3">
<span class="avatar-initial rounded-circle bg-label-success">U</span>
</div>
<div class="flex-grow-1 input-group input-group-merge rounded-pill">
<span class="input-group-text" id="basic-addon-search31">
<i class="ti ti-search"></i>
</span>
<input
type="text"
class="form-control chat-search-input"
placeholder="Buscar..."
aria-label="Buscar..."
aria-describedby="basic-addon-search31"/>
</div>
</div>
<i
class="ti ti-x cursor-pointer d-lg-none d-block position-absolute mt-2 me-1 top-0 end-0"
data-overlay
data-bs-toggle="sidebar"
data-target="#app-chat-contacts"></i>
</div>
<hr class="container-m-nx m-0"/>
<div class="sidebar-body">
<div class="chat-contact-list-item-title">
<h5 class="text-primary mb-0 px-4 pt-3 pb-2">Departamentos</h5>
</div>
<!-- Chats -->
<ul class="list-unstyled chat-contact-list" id="chat-list">
<li class="chat-contact-list-item chat-list-item-0 d-none">
<h6 class="text-muted mb-0">No Chats Found</h6>
</li>
<!-- CHAT LIST -->
</ul>
<!-- Contacts -->
<ul class="list-unstyled chat-contact-list mb-0" id="contact-list">
<li class="chat-contact-list-item chat-contact-list-item-title">
<h5 class="text-primary mb-0">Contacts</h5>
</li>
<li class="chat-contact-list-item contact-list-item-0 d-none">
<h6 class="text-muted mb-0">No Contacts Found</h6>
</li>
<!-- <li class="chat-contact-list-item">
<a class="d-flex align-items-center">
<div class="avatar d-block flex-shrink-0">
<span class="avatar-initial rounded-circle bg-label-primary">JJ</span>
</div>
<div class="chat-contact-info flex-grow-1 ms-2">
<h6 class="chat-contact-name text-truncate m-0">Jaime Jimenez</h6>
</div>
</a>
</li> -->
</ul>
</div>
</div>
<!-- /Chat contacts -->
<!-- Chat History -->
<div class="col app-chat-history bg-body">
<div class="chat-history-wrapper ">
<div class="chat-history-header border-bottom">
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex overflow-hidden align-items-center">
<i
class="ti ti-menu-2 ti-sm cursor-pointer d-lg-none d-block me-2"
data-bs-toggle="sidebar"
data-overlay
data-target="#app-chat-contacts"></i>
<div class="avatar d-block flex-shrink-0">
<span class="avatar-initial rounded-circle bg-label-primary">P</span>
</div>
<div class="chat-contact-info flex-grow-1 ms-2">
<h6 class="m-0">Departamento Producción</h6>
<small class="user-status text-muted">Consulta sobre el presupuesto P001</small>
</div>
</div>
<div class="d-flex align-items-center">
<div class="dropdown d-flex align-self-center">
<button
class="btn p-0"
type="button"
id="chat-header-actions"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<i class="ti ti-dots-vertical"></i>
</button>
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="chat-header-actions">
<a class="dropdown-item" href="javascript:void(0);">Silenciar Conversacion</a>
<a class="dropdown-item" href="javascript:void(0);">Limpiar Conversacion</a>
</div>
</div>
</div>
</div>
</div>
<div class="chat-history-body bg-body ">
<ul class="list-unstyled chat-history" id="chat-conversation">
</ul>
</div>
<!-- Chat message form -->
<div class="chat-history-footer shadow-sm">
<div class="form-send-message d-flex justify-content-between align-items-center">
<input
class="form-control message-input border-0 me-3 shadow-none"
placeholder="Type your message here"
/>
<div class="message-actions d-flex align-items-center">
<a class="btn btn-primary d-flex send-msg-btn ">
<i class="ti ti-send me-md-1 me-0"></i>
<span class="align-middle d-md-inline-block d-none">Enviar</span>
</a>
</div>
</div>
</div>
</div>
</div>
<!-- /Chat History -->
<div class="app-overlay"></div>
</div>
</div>
</div>
<?= $this->section('css') ?>
<link rel="stylesheet" href="<?= site_url('themes/vuexy/vendor/css/pages/app-chat.css') ?>">
<?= $this->endSection() ?>
<!------------------------------------------------------->
<!-- Código JS logica -->
<!------------------------------------------------------->
<?= $this->section("additionalExternalJs") ?>
<script type="module" src="<?= site_url('assets/js/safekat/pages/chatPresupuesto.js') ?>"></script>
<script src="<?= site_url('themes/vuexy/vendor/libs/perfect-scrollbar/perfect-scrollbar.js') ?>"></script>
<!-- <script src="<?= site_url('js_loader/chat_js') ?>"></script> -->
<?= $this->endSection() ?>

View File

@ -34,8 +34,9 @@
<?= view("themes/vuexy/form/presupuestos/cosidotapablanda/_datosServiciosItems") ?>
<?= view("themes/vuexy/form/presupuestos/cosidotapablanda/_datosEnvios") ?>
<?= view("themes/vuexy/form/presupuestos/cosidotapablanda/_comentariosItems") ?>
<?= view("themes/vuexy/components/chat_presupuesto",data:["modelId" => $presupuestoId]) ?>
<?= view("themes/vuexy/form/presupuestos/cosidotapablanda/_resumenPresupuestoItems") ?>
<?= view("themes/vuexy/form/presupuestos/cosidotapablanda/_tiradasAlternativasItems") ?>
<?= view("themes/vuexy/form/presupuestos/cosidotapablanda/_tiradasAlternativasItems") ?>
<?php else: ?>
<input type="hidden" name="total_presupuesto" id="total_presupuesto" class="form-control"
value="0.0"></input>
@ -367,6 +368,7 @@ $('#bc-save').on( "click", function() {
<?= $this->section('css') ?>
<link rel="stylesheet" href="<?= site_url('themes/vuexy/css/datatables-editor/editor.dataTables.min.css') ?>">
<link rel="stylesheet" href="<?= site_url("themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/buttons.bootstrap5.min.css") ?>">
<link rel="stylesheet" href="<?= site_url('themes/vuexy/vendor/css/pages/app-chat.css') ?>">
<link rel="stylesheet" href="<?= site_url('themes/vuexy/css/sk-datatables.css') ?>">
<?= $this->endSection() ?>
@ -390,5 +392,6 @@ $('#bc-save').on( "click", function() {
<script src="<?= site_url('js_loader/tiradasAlternativas_js') ?>"></script>
<script src="<?= site_url('js_loader/datosServicios_js') ?>"></script>
<script src="<?= site_url('themes/vuexy/vendor/libs/two/two.js') ?>"></script>
<script src="<?= site_url('themes/vuexy/vendor/libs/perfect-scrollbar/perfect-scrollbar.js') ?>"></script>
<?php endif; ?>
<?= $this->endSection() ?>

View File

@ -6,7 +6,7 @@ if (auth()->user()->inGroup('beta')) {
?>
<!-- Messages -->
<li class="menu-item">
<a href="<?= site_url("servicios/mensajes") ?>" class="menu-link">
<a href="<?= route_to('mensajeriaView') ?>" class="menu-link">
<i class="menu-icon tf-icons ti ti-message"></i>
<?= lang("App.menu_mensajes") ?>
</a>

View File

@ -0,0 +1,46 @@
class Ajax{
constructor(url, data, headers, success, error, type='default'){
this.url = url;
this.data = data;
this.headers = headers;
this.success = success;
this.error = error;
this.type = type;
}
post(){
(this.type == 'default') ? this.ajax('POST'): this.ajaxForm('POST');
}
get(){
this.ajax('GET');
}
put(){
(this.type == 'default') ? this.ajax('PUT'): this.ajaxForm('PUT');
}
delete(){
(this.type == 'default') ? this.ajax('DELETE'): this.ajaxForm('DELETE');
}
ajax(method){
$.ajax({
url : this.url,
type : method,
data: this.data,
headers: this.headers,
success: this.success,
error: this.error
})
}
ajaxForm(method){
$.ajax({
url : this.url,
type : method,
data: this.data,
processData: false,
contentType: false,
headers: this.headers,
success: this.success,
error: this.error
})
}
}
export default Ajax

View File

@ -0,0 +1,446 @@
import Ajax from '../components/ajax.js'
class Chat {
constructor(domItem) {
this.domItem = domItem
this.chatList = this.domItem.find("#chat-list")
this.chatHistory = this.domItem.find("#chat-conversation")
this.modelId = this.domItem.data("id")
this.chatHistoryBody = document.querySelector(".chat-history-body")
this.sendBtnMessageDepartment = this.domItem.find("#send-msg-btn-deparment")
this.sendBtnMessageInternal= this.domItem.find("#send-msg-btn-internal")
this.messageInput = this.domItem.find(".message-input")
this.sideBar = this.domItem.find(".sidebar-body")
this.chatDeparmentId = undefined
this.headers = {}
this.chatContactsBody = document.querySelector('.app-chat-contacts .sidebar-body')
this.chatContactListItems = [].slice.call(
document.querySelectorAll('.chat-contact-list-item:not(.chat-contact-list-item-title)')
)
}
init() {
// Inicializar PerfectScrollbar
this.sendBtnMessageDepartment.addClass("d-none")
this.sendBtnMessageInternal.addClass("d-none")
if (this.chatContactsBody) {
this.scrollbarContacts = new PerfectScrollbar(this.chatContactsBody, {
wheelPropagation: false,
suppressScrollX: true
});
}
if (this.chatHistoryBody) {
this.scrollbarChatHistory = new PerfectScrollbar(this.chatHistoryBody, {
wheelPropagation: false,
suppressScrollX: true
});
}
}
initGeneral() {
this.chatType = "general"
this._handleGetChatList()
this.sendBtnMessageDepartment.on("click", this._sendMessage.bind(this))
}
initPresupuesto() {
this.chatType = "presupuesto"
this._handleGetChatList()
this.sendBtnMessageDepartment.on("click", this._sendMessage.bind(this))
}
initPedido() {
this.chatType = "pedido"
this._handleGetChatList()
this.sendBtnMessageDepartment.on("click", this._sendMessage.bind(this))
}
initFactura() {
this.chatType = "factura"
this._handleGetChatList()
this.sendBtnMessageDepartment.on("click", this._sendMessage.bind(this))
}
initContacts() {
this.chatType = "internal"
}
_setBtnInternal()
{
this.sendBtnMessageInternal.removeClass("d-none")
this.sendBtnMessageDepartment.addClass("d-none")
}
_setBtnDeparment()
{
this.sendBtnMessageDepartment.removeClass("d-none")
this.sendBtnMessageInternal.addClass("d-none")
}
/**============================================
* PRESUPUESTOS
*=============================================**/
_handleGetChatList(event) {
let ajax = new Ajax(
"/chat/departments",
null,
null,
this._handleGetChatListSuccess.bind(this),
this._handleGetChatListError.bind(this),
);
ajax.get();
}
_handleGetChatListSuccess(data) {
Object.values(data).map(row => {
this.chatList.append(this._getContact(row))
this.chatList.find(`#chat_${row.name}`).on("click", (event) => {
let chatDeparmentId = this.chatList.find(`#chat_${row.name}`).data("id")
this.domItem.find(".chat-history-header div.chat-contact-info h6").text(row.display)
this.domItem.find(".chat-history-header div.chat-contact-info small.user-status").text(row.display)
this._getChatMessage(chatDeparmentId)
})
})
this.chatList.find(`#chat__produccion`).trigger("click");
}
_handleGetChatListError(error) {
console.error(error)
}
_getContact(row) {
let chat = `
<li class="chat-contact-list-item">
<a class="d-flex align-items-center" id="chat_${row.name}" data-id="${row.id}">
<div class="flex-shrink-0 avatar">
<span class="avatar-initial rounded-circle bg-label-success">${row.display.charAt(0)}</span>
</div>
<div class="chat-contact-info flex-grow-1 ms-2">
<h6 class="chat-contact-name text-truncate m-0">${row.display}</h6>
<p class="chat-contact-status text-muted text-truncate mb-0">
${row.display}
</p>
</div>
</a>
</li>
`
return chat
}
_getChatMessage(chatDeparmentId) {
this.chatDeparmentId = chatDeparmentId
let ajax = new Ajax(
`/chat/department/${this.chatType}/${this.chatDeparmentId}/${this.modelId}`,
null,
null,
this._getChatMessageSuccess.bind(this),
this._getChatMessageError.bind(this),
);
ajax.get();
}
_getChatMessageSuccess(data) {
this.chatHistory.empty()
this._setBtnDeparment()
if (data.messages) {
data.messages.map((m) => {
this._addChatMessage(m)
})
}
}
_getChatMessageError(err) {
console.log(err)
}
_addChatMessage(chatMessage, pos = "left") {
let chatItem = `
<li class="chat-message chat-message-${chatMessage.pos}">
<div class="d-flex overflow-hidden">
<div class="chat-message-wrapper flex-grow-1">
<div class="chat-message-text">
<p class="mb-0">${chatMessage?.message}</p>
</div>
<div class="text-end text-muted mt-1">
<div class="text-start text-muted mt-1">
<i class="ti me-1"></i>
<small>${chatMessage?.user?.first_name + " " + chatMessage?.user?.last_name}</small>
</div>
<i class="ti ti-checks ti-xs me-1 text-success"></i>
<small>${chatMessage.created_at}</small>
</div>
</div>
<div class="user-avatar flex-shrink-0 ms-3">
<div class="avatar avatar-sm">
<span class="avatar-initial rounded-circle bg-label-primary">${chatMessage?.user?.first_name.charAt(0) + chatMessage?.user?.last_name.charAt(0)}</span>
</div>
</div>
</div>
</li>
`
this.chatHistory.append(chatItem)
return chatItem
}
_sendMessage() {
let messageText = this.messageInput.val()
const body = {
message: messageText,
chat_department_id: this.chatDeparmentId,
user: this.userId,
model_id: this.modelId
}
if (messageText) {
let ajax = new Ajax(
`/chat/message/${this.chatType}`,
body,
null,
this._sendMessageSuccess.bind(this),
this._sendMessageError.bind(this),
);
ajax.post();
}
}
_sendMessageSuccess(data) {
this.messageInput.val("")
this._getChatMessage(this.chatDeparmentId)
}
_sendMessageError(err) {
console.error(err)
}
_handleListContacts() {
this.sideBar.find("#contact-list").removeClass("d-none")
this.sendBtnMessageInternal.on("click",this._sendMessageInternal.bind(this))
let ajax = new Ajax(
"/chat/contacts",
null,
null,
this._handleListContactsSuccess.bind(this),
this._handleListContactsError.bind(this)
)
ajax.get()
}
_handleListContactsSuccess(contacts) {
if (contacts) {
contacts.map((c) => {
this._addContactToList(c)
});
} else {
this.sideBar.find("#contact-list").removeClass("d-none")
}
this.sideBar.find(".contact-chat").on("click", (e) => {
let userId = $(e.currentTarget).data("id")
this.receiverId = userId
this._handleGetSingleContact(userId)
this._setBtnInternal()
this.chatHistory.empty()
})
}
_handleListContactsError(err) {
console.error(err)
}
_handleGetSingleContact(userId) {
let ajax = new Ajax(
`/chat/contacts/${userId}`,
null,
null,
this._handleGetSingleContactSuccess.bind(this),
this._handleGetSingleContactError.bind(this),
)
ajax.get()
}
_handleGetSingleContactSuccess(contact) {
this.domItem.find(".chat-history-header div.chat-contact-info h6").text([contact.first_name, contact.last_name].join(" "))
this.domItem.find(".chat-history-header div.chat-contact-info small.user-status").text(contact.username)
let ajax = new Ajax(
`/chat/contact/${contact.id}/messages`,
null,
null,
this._handleGetSingleContactMessagesSuccess.bind(this),
this._handleGetSingleContactMessagesError.bind(this)
)
ajax.get()
}
_handleGetSingleContactMessagesSuccess(data) {
if (data) {
data.map((m) => {
this._addChatMessage(m)
})
}
}
_handleGetSingleContactMessagesError(err) { }
_handleGetSingleContactError(err) {
}
_sendMessageInternal() {
let messageText = this.messageInput.val()
const body = {
message: messageText,
receiver_id: this.receiverId,
}
if (messageText) {
let ajax = new Ajax(
`/chat/message/internal`,
body,
null,
this._sendMessageInternalSuccess.bind(this),
this._sendMessageInternalError.bind(this),
);
ajax.post();
}
}
_sendMessageInternalSuccess(message) {
this.messageInput.val("")
this._handleGetSingleContact(this.receiverId)
}
_sendMessageInternalError(err) {
console.error(err)
}
_addContactToList(contact) {
let contactItem =
`
<li class="chat-contact-list-item">
<a class="d-flex align-items-center contact-chat" data-id="${contact.id}"
id="chat-contact-list-item-${contact.id}">
<div class="avatar d-block flex-shrink-0">
<span class="avatar-initial rounded-circle bg-label-primary">
${contact?.first_name?.charAt(0) ?? "?"
+ contact?.last_name?.charAt(0) ?? "?"}</span>
</div>
<div class="chat-contact-info flex-grow-1 ms-2">
<h6 class="chat-contact-name text-truncate m-0">${contact?.first_name ?? "" + " " +
contact?.last_name ?? ""}</h6>
<p class="chat-contact-status text-muted text-truncate mb-0">
${contact.username}
</p>
</div>
${contact.unreadMessages ? `<span class="badge badge-center rounded-pill bg-primary">${contact.unreadMessages}</span>` : ""}
</a>
</li>
`
if (contact.first_name || contact.last_name) {
this.sideBar.find("#contact-list").append(contactItem)
}
}
}
export default Chat
// // Seleccionar elementos del DOM
// const chatContactsBody = document.querySelector('.app-chat-contacts .sidebar-body'),
// chatContactListItems = [].slice.call(
// document.querySelectorAll('.chat-contact-list-item:not(.chat-contact-list-item-title)')
// ),
// chatHistoryBody = document.querySelector('.chat-history-body'),
// chatSidebarLeftUserAbout = document.querySelector('.chat-sidebar-left-user-about'),
// messageInput = document.querySelector('.message-input'),
// searchInput = document.querySelector('.chat-search-input'),
// sendMsgBtn = document.querySelector('.send-msg-btn'); // Seleccionar el botón de envío de mensaje
// // Inicializar PerfectScrollbar
// if (chatContactsBody) {
// new PerfectScrollbar(chatContactsBody, {
// wheelPropagation: false,
// suppressScrollX: true
// });
// }
// if (chatHistoryBody) {
// new PerfectScrollbar(chatHistoryBody, {
// wheelPropagation: false,
// suppressScrollX: true
// });
// }
// // Función para desplazar el scroll al final
// function scrollToBottom() {
// if (chatHistoryBody) {
// chatHistoryBody.scrollTo(0, chatHistoryBody.scrollHeight);
// }
// }
// scrollToBottom();
// // Seleccionar chat o contacto
// // Filtrar chats
// if (searchInput) {
// searchInput.addEventListener('keyup', e => {
// const searchValue = e.currentTarget.value.toLowerCase(),
// chatListItem0 = document.querySelector('.chat-list-item-0'),
// contactListItem0 = document.querySelector('.contact-list-item-0'),
// searchChatListItems = [].slice.call(
// document.querySelectorAll('#chat-list li:not(.chat-contact-list-item-title)')
// ),
// searchContactListItems = [].slice.call(
// document.querySelectorAll('#contact-list li:not(.chat-contact-list-item-title)')
// );
// // Buscar en chats
// const chatListItemsCount = searchChatContacts(searchChatListItems, searchValue);
// // Mostrar u ocultar mensaje de "No se encontraron resultados" en chats
// if (chatListItem0) {
// chatListItem0.classList.toggle('d-none', chatListItemsCount !== 0);
// }
// // Buscar en contactos
// const contactListItemsCount = searchChatContacts(searchContactListItems, searchValue);
// // Mostrar u ocultar mensaje de "No se encontraron resultados" en contactos
// if (contactListItem0) {
// contactListItem0.classList.toggle('d-none', contactListItemsCount !== 0);
// }
// });
// }
// // Función para buscar en chats y contactos
// function searchChatContacts(searchListItems, searchValue) {
// let searchListItemsCount = 0;
// searchListItems.forEach(searchListItem => {
// const searchListItemText = searchListItem.textContent.toLowerCase();
// const matchesSearch = searchListItemText.indexOf(searchValue) !== -1;
// searchListItem.classList.toggle('d-flex', matchesSearch);
// searchListItem.classList.toggle('d-none', !matchesSearch);
// if (matchesSearch) {
// searchListItemsCount++;
// }
// });
// return searchListItemsCount;
// }
// // // Enviar mensaje
// // if (sendMsgBtn) {
// // sendMsgBtn.addEventListener('click', e => {
// // e.preventDefault();
// // if (messageInput.value) {
// // const renderMsg = document.createElement('div');
// // renderMsg.className = 'chat-message-text mt-2';
// // renderMsg.innerHTML = `<p class="mb-0">${messageInput.value}</p>`;
// // const lastChatMessageWrapper = document.querySelector('li:last-child .chat-message-wrapper');
// // if (lastChatMessageWrapper) {
// // lastChatMessageWrapper.appendChild(renderMsg);
// // }
// // messageInput.value = '';
// // scrollToBottom();
// // }
// // });
// // }

View File

@ -0,0 +1,6 @@
import Chat from '../components/chat.js'
let chat = new Chat($("#chat-factura"))
chat.init()
chat.initFactura()
chat._handleListContacts()

View File

@ -0,0 +1,5 @@
import Chat from '../components/chat.js'
let chat = new Chat($("#chat-general"))
chat.init()
chat._handleListContacts()

View File

@ -0,0 +1,6 @@
import Chat from '../components/chat.js'
let chat = new Chat($("#chat-pedido"))
chat.init()
chat.initPedido()
chat._handleListContacts()

View File

@ -0,0 +1,6 @@
import Chat from '../components/chat.js'
let chat = new Chat($("#chat-presupuesto"))
chat.init()
chat.initPresupuesto()
chat._handleListContacts()

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -2,6 +2,9 @@
position: relative;
height: calc(100vh - 11.5rem) !important;
}
a.send-msg-btn {
color: white
}
@media (min-width: 1200px) {
.layout-horizontal .app-chat {
height: calc(100vh - 11.5rem - 2.2rem) !important;
@ -57,6 +60,7 @@
height: calc(calc(100vh - 11.5rem) - 5rem - 2.2rem);
}
}
.app-chat .app-chat-contacts .sidebar-body .chat-contact-list li.chat-contact-list-item {
display: flex;
justify-content: space-between;