From 766347ad81f3c996ecb6344975fa9054a2d8d852 Mon Sep 17 00:00:00 2001 From: amazuecos Date: Mon, 23 Sep 2024 09:19:45 +0200 Subject: [PATCH] feat: chat module --- ci4/app/Config/Routes.php | 21 + ci4/app/Controllers/Chat/ChatController.php | 189 ++++++++ .../Mensajeria/MensajesDirectos.php | 18 +- .../2024-09-17-151435_ChatDepartments.php | 9 + .../2024-09-17-151440_ChatsTable.php | 19 + .../2024-09-17-151450_ChatMessages.php | 16 +- ci4/app/Database/Seeds/ChatSeeder.php | 12 +- ci4/app/Language/es/Chat.php | 5 + ci4/app/Models/Chat/ChatDeparmentModel.php | 54 ++- ci4/app/Models/Chat/ChatMessageModel.php | 78 ++- ci4/app/Models/Chat/ChatModel.php | 148 +++++- .../Models/Presupuestos/PresupuestoModel.php | 3 + .../themes/vuexy/components/chat_factura.php | 161 +++++++ .../themes/vuexy/components/chat_general.php | 0 .../themes/vuexy/components/chat_pedido.php | 164 +++++++ .../vuexy/components/chat_presupuesto.php | 156 ++++++ .../themes/vuexy/form/mensajes/mensajeria.js | 92 ++-- .../vuexy/form/mensajes/mensajesView.php | 350 +------------- .../vuexy/form/pedidos/viewPedidoForm.php | 6 +- .../cosidotapablanda/_mensajeria.php | 388 +-------------- .../viewCosidotapablandaForm.php | 2 +- httpdocs/assets/js/safekat/components/ajax.js | 46 ++ httpdocs/assets/js/safekat/components/chat.js | 446 ++++++++++++++++++ .../assets/js/safekat/pages/chatFactura.js | 4 + .../assets/js/safekat/pages/chatPedido.js | 5 + .../js/safekat/pages/chatPresupuesto.js | 6 + httpdocs/themes/vuexy/img/avatars/user.png | Bin 0 -> 19293 bytes .../vuexy/vendor/css/pages/app-chat.css | 1 + 28 files changed, 1598 insertions(+), 801 deletions(-) create mode 100644 ci4/app/Controllers/Chat/ChatController.php create mode 100644 ci4/app/Language/es/Chat.php create mode 100644 ci4/app/Views/themes/vuexy/components/chat_factura.php create mode 100644 ci4/app/Views/themes/vuexy/components/chat_general.php create mode 100644 ci4/app/Views/themes/vuexy/components/chat_pedido.php create mode 100644 ci4/app/Views/themes/vuexy/components/chat_presupuesto.php create mode 100644 httpdocs/assets/js/safekat/components/ajax.js create mode 100644 httpdocs/assets/js/safekat/components/chat.js create mode 100644 httpdocs/assets/js/safekat/pages/chatFactura.js create mode 100644 httpdocs/assets/js/safekat/pages/chatPedido.js create mode 100644 httpdocs/assets/js/safekat/pages/chatPresupuesto.js create mode 100644 httpdocs/themes/vuexy/img/avatars/user.png diff --git a/ci4/app/Config/Routes.php b/ci4/app/Config/Routes.php index dc673f87..5bed71a5 100644 --- a/ci4/app/Config/Routes.php +++ b/ci4/app/Config/Routes.php @@ -737,7 +737,28 @@ $routes->group('mensajes', ['namespace' => 'App\Controllers\Mensajeria'], functi }); }); +$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']); + + + + + + + +}); /* * -------------------------------------------------------------------- diff --git a/ci4/app/Controllers/Chat/ChatController.php b/ci4/app/Controllers/Chat/ChatController.php new file mode 100644 index 00000000..775ae028 --- /dev/null +++ b/ci4/app/Controllers/Chat/ChatController.php @@ -0,0 +1,189 @@ +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); + } +} diff --git a/ci4/app/Controllers/Mensajeria/MensajesDirectos.php b/ci4/app/Controllers/Mensajeria/MensajesDirectos.php index e951ffc1..ff1e8686 100644 --- a/ci4/app/Controllers/Mensajeria/MensajesDirectos.php +++ b/ci4/app/Controllers/Mensajeria/MensajesDirectos.php @@ -36,23 +36,7 @@ class MensajesDirectos extends BaseResourceController public function index() { - - // Modelos - $participantModel = model('App\Models\Mensajeria\ParticipanteModel'); - - - $viewData = [ - 'pageSubTitle' => lang('Basic.global.ManageAllRecords', [lang('Paises.pais')]), - 'conversacionesEntity' => new ConversacionEntity(), - 'usingServerSideDataTable' => true, - ]; - - //$viewData['conversaciones'] = $participantModel->getChatsByUser(auth()->user()->id); - $viewData['conversaciones'] = $participantModel->getChatsByUser(639); - - $viewData = array_merge($this->viewData, $viewData); // merge any possible values from the parent controller class - - return view(static::$viewPath . static::$indexRoute, $viewData); + return view(static::$viewPath . static::$indexRoute); } diff --git a/ci4/app/Database/Migrations/2024-09-17-151435_ChatDepartments.php b/ci4/app/Database/Migrations/2024-09-17-151435_ChatDepartments.php index 52bc6437..0993133e 100644 --- a/ci4/app/Database/Migrations/2024-09-17-151435_ChatDepartments.php +++ b/ci4/app/Database/Migrations/2024-09-17-151435_ChatDepartments.php @@ -22,6 +22,15 @@ class ChatDepartments extends Migration "type" => "VARCHAR", "constraint" => '255', ], + "description" => [ + "type" => "TEXT", + "null" => true, + ], + "type" => [ + "type" => "ENUM", + 'constraint' => ['general', 'presupuesto', 'pedido'], + 'default' => 'general', + ], ]; public function up() diff --git a/ci4/app/Database/Migrations/2024-09-17-151440_ChatsTable.php b/ci4/app/Database/Migrations/2024-09-17-151440_ChatsTable.php index 25ac44de..f4b69606 100644 --- a/ci4/app/Database/Migrations/2024-09-17-151440_ChatsTable.php +++ b/ci4/app/Database/Migrations/2024-09-17-151440_ChatsTable.php @@ -16,11 +16,25 @@ class ChatsTable extends Migration "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 ], @@ -31,6 +45,7 @@ class ChatsTable extends Migration $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([ @@ -51,6 +66,10 @@ class ChatsTable extends Migration $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); } diff --git a/ci4/app/Database/Migrations/2024-09-17-151450_ChatMessages.php b/ci4/app/Database/Migrations/2024-09-17-151450_ChatMessages.php index 6ce55b86..289918d5 100644 --- a/ci4/app/Database/Migrations/2024-09-17-151450_ChatMessages.php +++ b/ci4/app/Database/Migrations/2024-09-17-151450_ChatMessages.php @@ -17,14 +17,25 @@ class ChatMessages extends Migration "unsigned" => true ], - "user_id" => [ + "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, + ], ]; @@ -51,7 +62,8 @@ class ChatMessages extends Migration ], ]); $this->forge->addPrimaryKey('id'); - $this->forge->addForeignKey(['user_id'], 'users', ['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); } diff --git a/ci4/app/Database/Seeds/ChatSeeder.php b/ci4/app/Database/Seeds/ChatSeeder.php index a6fd6c2e..28df32ab 100644 --- a/ci4/app/Database/Seeds/ChatSeeder.php +++ b/ci4/app/Database/Seeds/ChatSeeder.php @@ -18,7 +18,8 @@ class ChatSeeder extends Seeder "users" => [ "mbalbaci@safekat.com", "mari.cano@safekat.com", - "beatriz@safekat.com" + "beatriz@safekat.com", + "imnavajas@coit.es", ], ], [ @@ -26,6 +27,7 @@ class ChatSeeder extends Seeder "display" => "POD", "users" => [ "pod@safekat.com", + "imnavajas@coit.es", ], ], [ @@ -33,6 +35,7 @@ class ChatSeeder extends Seeder "display" => "Maquetación", "users" => [ "maquetacion@safekat.com", + "imnavajas@coit.es", ], ], // [ @@ -54,6 +57,7 @@ class ChatSeeder extends Seeder "display" => "Logística", "users" => [ "logistica@safekat.com", + "imnavajas@coit.es", ], ], [ @@ -61,19 +65,21 @@ class ChatSeeder extends Seeder "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", $mail)->first(); + $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]); } } diff --git a/ci4/app/Language/es/Chat.php b/ci4/app/Language/es/Chat.php new file mode 100644 index 00000000..3981f2ce --- /dev/null +++ b/ci4/app/Language/es/Chat.php @@ -0,0 +1,5 @@ + "Chat" +]; \ No newline at end of file diff --git a/ci4/app/Models/Chat/ChatDeparmentModel.php b/ci4/app/Models/Chat/ChatDeparmentModel.php index 16a94953..4558bbd3 100644 --- a/ci4/app/Models/Chat/ChatDeparmentModel.php +++ b/ci4/app/Models/Chat/ChatDeparmentModel.php @@ -2,6 +2,7 @@ namespace App\Models\Chat; +use App\Models\Usuarios\UserModel; use CodeIgniter\Model; class ChatDeparmentModel extends Model @@ -14,7 +15,9 @@ class ChatDeparmentModel extends Model protected $protectFields = true; protected $allowedFields = [ "name", - "display" + "display", + "description", + "type" ]; protected bool $allowEmptyInserts = false; @@ -46,4 +49,53 @@ class ChatDeparmentModel extends Model 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; + } } diff --git a/ci4/app/Models/Chat/ChatMessageModel.php b/ci4/app/Models/Chat/ChatMessageModel.php index 6d030205..e9f72308 100644 --- a/ci4/app/Models/Chat/ChatMessageModel.php +++ b/ci4/app/Models/Chat/ChatMessageModel.php @@ -2,7 +2,7 @@ namespace App\Models\Chat; - +use App\Models\Usuarios\UserModel; use CodeIgniter\Model; class ChatMessageModel extends Model @@ -13,7 +13,13 @@ class ChatMessageModel extends Model protected $returnType = 'array'; protected $useSoftDeletes = false; protected $protectFields = true; - protected $allowedFields = []; + protected $allowedFields = [ + "message", + "chat_id", + "sender_id", + "receiver_id", + "viewed" + ]; protected bool $allowEmptyInserts = false; protected bool $updateOnlyChanged = true; @@ -44,4 +50,72 @@ class ChatMessageModel extends Model 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; + } } diff --git a/ci4/app/Models/Chat/ChatModel.php b/ci4/app/Models/Chat/ChatModel.php index 5927fc27..65cf010e 100644 --- a/ci4/app/Models/Chat/ChatModel.php +++ b/ci4/app/Models/Chat/ChatModel.php @@ -4,6 +4,7 @@ namespace App\Models\Chat; use CodeIgniter\Model; +use stdClass; class ChatModel extends Model { @@ -15,7 +16,9 @@ class ChatModel extends Model protected $protectFields = true; protected $allowedFields = [ "pedido_id", - "chat_department_id" + "chat_department_id", + "presupuesto_id", + "factura_id", ]; protected bool $allowEmptyInserts = false; @@ -49,22 +52,145 @@ class ChatModel extends Model protected $afterDelete = []; - public function getChat(int $chat_id) + public function getChat(int $chat_id): array { - $this->db->table('chats') + return $this->db->table('chats') ->select( [ - "chats.id as chatId", - "users.id as userId", - "chats.pedido_id as pedidoId", - "users.email", + "chats.*", "chat_messages.created_at as messageCreatedAt", "chat_messages.message as messageText", ] ) - ->join("users", "users.id == chat_messages.user_id", "left") - ->join("chat_deparments", "chat_deparments.id == chats.chat_deparment_id", "left") - ->join("chat_messages", "chats.id == chat_messages.chat_id", "left") - ->where("chatId", $chat_id)->get()->getResultObject(); + ->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; + } + } diff --git a/ci4/app/Models/Presupuestos/PresupuestoModel.php b/ci4/app/Models/Presupuestos/PresupuestoModel.php index 85eb6e8f..0150f6af 100755 --- a/ci4/app/Models/Presupuestos/PresupuestoModel.php +++ b/ci4/app/Models/Presupuestos/PresupuestoModel.php @@ -781,4 +781,7 @@ class PresupuestoModel extends \App\Models\BaseModel return $description_interior . $description_cubierta . $description_sobrecubierta . $acabado; } + + + } diff --git a/ci4/app/Views/themes/vuexy/components/chat_factura.php b/ci4/app/Views/themes/vuexy/components/chat_factura.php new file mode 100644 index 00000000..cdfa7115 --- /dev/null +++ b/ci4/app/Views/themes/vuexy/components/chat_factura.php @@ -0,0 +1,161 @@ +
+
+

+ +

+
+
+
+
+
+ + +
+ +
+ +
+ + + +
+
+
+
+
+ +
+ P +
+
+
Departamento Producción
+ Consulta sobre el presupuesto P001 +
+
+ +
+
+
+
    + + +
+
+ + +
+
+ + +
+
+
+
+
+
+ +
+
+section('css') ?> + +endSection() ?> + + + +section("additionalExternalJs") ?> + + + +endSection() ?> \ No newline at end of file diff --git a/ci4/app/Views/themes/vuexy/components/chat_general.php b/ci4/app/Views/themes/vuexy/components/chat_general.php new file mode 100644 index 00000000..e69de29b diff --git a/ci4/app/Views/themes/vuexy/components/chat_pedido.php b/ci4/app/Views/themes/vuexy/components/chat_pedido.php new file mode 100644 index 00000000..0a77facf --- /dev/null +++ b/ci4/app/Views/themes/vuexy/components/chat_pedido.php @@ -0,0 +1,164 @@ +
+
+

+ +

+
+
+
+ +
+
+ + +
+ +
+ +
+ + + +
+
+
+
+
+ +
+ P +
+
+
+ +
+
+ +
+
+
+
    + + +
+
+ + +
+
+ + +
+
+
+
+
+
+
+
+ + + +section('css') ?> + +endSection() ?> + + + +section("additionalExternalJs") ?> + + + +endSection() ?> \ No newline at end of file diff --git a/ci4/app/Views/themes/vuexy/components/chat_presupuesto.php b/ci4/app/Views/themes/vuexy/components/chat_presupuesto.php new file mode 100644 index 00000000..78e97c68 --- /dev/null +++ b/ci4/app/Views/themes/vuexy/components/chat_presupuesto.php @@ -0,0 +1,156 @@ +
+
+

+ +

+
+
+
+
+
+ + +
+ +
+ +
+ + + +
+
+
+
+
+ +
+ P +
+
+
Departamento Producción
+ Consulta sobre el presupuesto P001 +
+
+ +
+
+
+
    + + +
+
+ + +
+
+ + +
+
+
+
+
+
+
+
+ +section('css') ?> + +endSection() ?> + + + +section("additionalExternalJs") ?> + + + +endSection() ?> \ No newline at end of file diff --git a/ci4/app/Views/themes/vuexy/form/mensajes/mensajeria.js b/ci4/app/Views/themes/vuexy/form/mensajes/mensajeria.js index 89395845..4e7667b8 100644 --- a/ci4/app/Views/themes/vuexy/form/mensajes/mensajeria.js +++ b/ci4/app/Views/themes/vuexy/form/mensajes/mensajeria.js @@ -86,53 +86,53 @@ function searchChatContacts(searchListItems, searchValue) { // Enviar mensaje if (sendMsgBtn.length) { - sendMsgBtn.on('click', function (e) { - e.preventDefault(); - if (messageInput.val()) { - const renderMsg = $('
').addClass('chat-message-text mt-2').html(`

${messageInput.val()}

`); - const lastChatMessageWrapper = $('li:last-child .chat-message-wrapper'); - if (lastChatMessageWrapper.length) { - lastChatMessageWrapper.append(renderMsg); - } - messageInput.val(''); - scrollToBottom(); - } - }); + // sendMsgBtn.on('click', function (e) { + // e.preventDefault(); + // if (messageInput.val()) { + // const renderMsg = $('
').addClass('chat-message-text mt-2').html(`

${messageInput.val()}

`); + // const lastChatMessageWrapper = $('li:last-child .chat-message-wrapper'); + // if (lastChatMessageWrapper.length) { + // lastChatMessageWrapper.append(renderMsg); + // } + // messageInput.val(''); + // scrollToBottom(); + // } + // }); } -// Seleccionar los elementos

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

- var conversationId = $(this).attr('id'); +// // Seleccionar los elementos

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

+// var conversationId = $(this).attr('id'); - console.log(conversationId) +// 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 = ` -

- ${person.user_id} -
`; - $('#participants-container').append(participantHtml); - }); - } - }, - error: function (jqXHR, textStatus, errorThrown) { - // Manejar errores en la llamada AJAX - console.error('Error en la llamada AJAX:', textStatus, errorThrown); - } - }); -}) -; \ No newline at end of file +// // 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 = ` +//
+// ${person.user_id} +//
`; +// $('#participants-container').append(participantHtml); +// }); +// } +// }, +// error: function (jqXHR, textStatus, errorThrown) { +// // Manejar errores en la llamada AJAX +// console.error('Error en la llamada AJAX:', textStatus, errorThrown); +// } +// }); +// }) +// ; \ No newline at end of file diff --git a/ci4/app/Views/themes/vuexy/form/mensajes/mensajesView.php b/ci4/app/Views/themes/vuexy/form/mensajes/mensajesView.php index 8d330963..77c7cbe1 100644 --- a/ci4/app/Views/themes/vuexy/form/mensajes/mensajesView.php +++ b/ci4/app/Views/themes/vuexy/form/mensajes/mensajesView.php @@ -5,360 +5,12 @@ section('content'); ?>
-
-
- -
- -
- -
- - - -
-
-
-
-
-
- P -
-
- - -
-
-
-
    -
  • -
    -
    -
    -

    How can we help? We're here for you! 😄

    -
    -
    - - 10:00 AM -
    -
    -
    -
    - Avatar -
    -
    -
    -
  • -
  • -
    -
    -
    - Avatar -
    -
    -
    -
    -

    Hey John, I am looking for the best admin template.

    -

    Could you please help me to find it out? 🤔

    -
    -
    -

    It should be Bootstrap 5 compatible.

    -
    -
    - 10:02 AM -
    -
    -
    -
  • -
  • -
    -
    -
    -

    Vuexy has all the components you'll ever need in a - app.

    -
    -
    - - 10:03 AM -
    -
    -
    -
    - Avatar -
    -
    -
    -
  • -
  • -
    -
    -
    - Avatar -
    -
    -
    -
    -

    Looks clean and fresh UI. 😃

    -
    -
    -

    It's perfect for my next project.

    -
    -
    -

    How can I purchase it?

    -
    -
    - 10:05 AM -
    -
    -
    -
  • -
  • -
    -
    -
    -

    Thanks, you can purchase it.

    -
    -
    - - 10:06 AM -
    -
    -
    -
    - Avatar -
    -
    -
    -
  • -
  • -
    -
    -
    - Avatar -
    -
    -
    -
    -

    I will purchase it for sure. 👍

    -
    -
    -

    Thanks.

    -
    -
    - 10:08 AM -
    -
    -
    -
  • -
  • -
    -
    -
    -

    Great, Feel free to get in touch.

    -
    -
    - - 10:10 AM -
    -
    -
    -
    - Avatar -
    -
    -
    -
  • -
  • -
    -
    -
    - Avatar -
    -
    -
    -
    -

    Do you have design files for Vuexy?

    -
    -
    - 10:15 AM -
    -
    -
    -
  • -
  • -
    -
    -
    -

    - Yes that's correct documentation file, Design files are included - with - the template. -

    -
    -
    - - 10:15 AM -
    -
    -
    -
    - Avatar -
    -
    -
    -
  • -
-
- - -
-
- -
-
+ null]) ?>
endSection() ?> -section('additionalInlineJs') ?> -endSection() ?> - - -section('css') ?> - -endSection() ?> - - -section('additionalExternalJs') ?> - - -endSection() ?> diff --git a/ci4/app/Views/themes/vuexy/form/pedidos/viewPedidoForm.php b/ci4/app/Views/themes/vuexy/form/pedidos/viewPedidoForm.php index fc16989d..78102e6e 100644 --- a/ci4/app/Views/themes/vuexy/form/pedidos/viewPedidoForm.php +++ b/ci4/app/Views/themes/vuexy/form/pedidos/viewPedidoForm.php @@ -19,8 +19,9 @@ user()->inGroup('cliente-admin') || auth()->user()->inGroup('cliente-editor'))) : ?> - - + + + $pedidoEntity->id]) ?>
"btn btn-secondary float-start"]) ?> @@ -30,6 +31,7 @@
+ endSection() ?> diff --git a/ci4/app/Views/themes/vuexy/form/presupuestos/cosidotapablanda/_mensajeria.php b/ci4/app/Views/themes/vuexy/form/presupuestos/cosidotapablanda/_mensajeria.php index 86abbe58..0e57e675 100644 --- a/ci4/app/Views/themes/vuexy/form/presupuestos/cosidotapablanda/_mensajeria.php +++ b/ci4/app/Views/themes/vuexy/form/presupuestos/cosidotapablanda/_mensajeria.php @@ -1,4 +1,4 @@ -
+
@@ -33,56 +33,14 @@
@@ -121,7 +69,7 @@
-
+
@@ -157,191 +105,10 @@
-
-
    -
  • -
    -
    -
    -

    How can we help? We're here for you! 😄

    -
    -
    - - 10:00 AM -
    -
    -
    -
    - Avatar -
    -
    -
    -
  • -
  • -
    -
    -
    - Avatar -
    -
    -
    -
    -

    Hey John, I am looking for the best admin template.

    -

    Could you please help me to find it out? 🤔

    -
    -
    -

    It should be Bootstrap 5 compatible.

    -
    -
    - 10:02 AM -
    -
    -
    -
  • -
  • -
    -
    -
    -

    Vuexy has all the components you'll ever need in a app.

    -
    -
    - - 10:03 AM -
    -
    -
    -
    - Avatar -
    -
    -
    -
  • -
  • -
    -
    -
    - Avatar -
    -
    -
    -
    -

    Looks clean and fresh UI. 😃

    -
    -
    -

    It's perfect for my next project.

    -
    -
    -

    How can I purchase it?

    -
    -
    - 10:05 AM -
    -
    -
    -
  • -
  • -
    -
    -
    -

    Thanks, you can purchase it.

    -
    -
    - - 10:06 AM -
    -
    -
    -
    - Avatar -
    -
    -
    -
  • -
  • -
    -
    -
    - Avatar -
    -
    -
    -
    -

    I will purchase it for sure. 👍

    -
    -
    -

    Thanks.

    -
    -
    - 10:08 AM -
    -
    -
    -
  • -
  • -
    -
    -
    -

    Great, Feel free to get in touch.

    -
    -
    - - 10:10 AM -
    -
    -
    -
    - Avatar -
    -
    -
    -
  • -
  • -
    -
    -
    - Avatar -
    -
    -
    -
    -

    Do you have design files for Vuexy?

    -
    -
    - 10:15 AM -
    -
    -
    -
  • -
  • -
    -
    -
    -

    - Yes that's correct documentation file, Design files are included with - the template. -

    -
    -
    - - 10:15 AM -
    -
    -
    -
    - Avatar -
    -
    -
    -
  • +
    +
      + +
    @@ -352,9 +119,10 @@ placeholder="Type your message here" />
@@ -368,131 +136,17 @@
- +section('css') ?> + +endSection() ?> -section("additionalInlineJs") ?> - -/** - * App 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 -chatContactListItems.forEach(chatContactListItem => { - chatContactListItem.addEventListener('click', e => { - chatContactListItems.forEach(item => { - item.classList.remove('active'); - }); - e.currentTarget.classList.add('active'); - }); -}); - -// 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 = `

${messageInput.value}

`; - const lastChatMessageWrapper = document.querySelector('li:last-child .chat-message-wrapper'); - if (lastChatMessageWrapper) { - lastChatMessageWrapper.appendChild(renderMsg); - } - messageInput.value = ''; - scrollToBottom(); - } - }); -} - - - - - - - +section("additionalExternalJs") ?> + + + endSection() ?> + diff --git a/ci4/app/Views/themes/vuexy/form/presupuestos/cosidotapablanda/viewCosidotapablandaForm.php b/ci4/app/Views/themes/vuexy/form/presupuestos/cosidotapablanda/viewCosidotapablandaForm.php index 5d3c296c..638cc728 100644 --- a/ci4/app/Views/themes/vuexy/form/presupuestos/cosidotapablanda/viewCosidotapablandaForm.php +++ b/ci4/app/Views/themes/vuexy/form/presupuestos/cosidotapablanda/viewCosidotapablandaForm.php @@ -34,9 +34,9 @@ + $presupuestoId]) ?> - diff --git a/httpdocs/assets/js/safekat/components/ajax.js b/httpdocs/assets/js/safekat/components/ajax.js new file mode 100644 index 00000000..9045d117 --- /dev/null +++ b/httpdocs/assets/js/safekat/components/ajax.js @@ -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 \ No newline at end of file diff --git a/httpdocs/assets/js/safekat/components/chat.js b/httpdocs/assets/js/safekat/components/chat.js new file mode 100644 index 00000000..67136447 --- /dev/null +++ b/httpdocs/assets/js/safekat/components/chat.js @@ -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 = ` +
  • + +
    + ${row.display.charAt(0)} +
    +
    +
    ${row.display}
    +

    + ${row.display} +

    +
    +
    +
  • + ` + 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 = ` +
  • +
    +
    + +
    +

    ${chatMessage?.message}

    +
    +
    +
    + + ${chatMessage?.user?.first_name + " " + chatMessage?.user?.last_name} +
    + + ${chatMessage.created_at} +
    + +
    +
    +
    + ${chatMessage?.user?.first_name.charAt(0) + chatMessage?.user?.last_name.charAt(0)} +
    +
    +
    +
  • + ` + 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 = + ` +
  • + +
    + + ${contact?.first_name?.charAt(0) ?? "?" + + contact?.last_name?.charAt(0) ?? "?"} + +
    +
    +
    ${contact?.first_name ?? "" + " " + + contact?.last_name ?? ""}
    +

    + ${contact.username} +

    +
    + ${contact.unreadMessages ? `${contact.unreadMessages}` : ""} +
    +
  • + ` + + 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 = `

    ${messageInput.value}

    `; +// // const lastChatMessageWrapper = document.querySelector('li:last-child .chat-message-wrapper'); +// // if (lastChatMessageWrapper) { +// // lastChatMessageWrapper.appendChild(renderMsg); +// // } +// // messageInput.value = ''; +// // scrollToBottom(); +// // } +// // }); +// // } diff --git a/httpdocs/assets/js/safekat/pages/chatFactura.js b/httpdocs/assets/js/safekat/pages/chatFactura.js new file mode 100644 index 00000000..76102699 --- /dev/null +++ b/httpdocs/assets/js/safekat/pages/chatFactura.js @@ -0,0 +1,4 @@ +import Chat from '../components/chat.js' + +let chat = new Chat($("#chat-factura")) +chat.initFactura() \ No newline at end of file diff --git a/httpdocs/assets/js/safekat/pages/chatPedido.js b/httpdocs/assets/js/safekat/pages/chatPedido.js new file mode 100644 index 00000000..90e25c42 --- /dev/null +++ b/httpdocs/assets/js/safekat/pages/chatPedido.js @@ -0,0 +1,5 @@ +import Chat from '../components/chat.js' + +let chat = new Chat($("#chat-pedido")) +chat.init() +chat.initPedido() \ No newline at end of file diff --git a/httpdocs/assets/js/safekat/pages/chatPresupuesto.js b/httpdocs/assets/js/safekat/pages/chatPresupuesto.js new file mode 100644 index 00000000..4a09c66d --- /dev/null +++ b/httpdocs/assets/js/safekat/pages/chatPresupuesto.js @@ -0,0 +1,6 @@ +import Chat from '../components/chat.js' + +let chat = new Chat($("#chat-presupuesto")) +chat.init() +chat.initPresupuesto() +chat._handleListContacts() \ No newline at end of file diff --git a/httpdocs/themes/vuexy/img/avatars/user.png b/httpdocs/themes/vuexy/img/avatars/user.png new file mode 100644 index 0000000000000000000000000000000000000000..2b8b658355da59697e17ce72946d1f9bb1642631 GIT binary patch literal 19293 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 literal 0 HcmV?d00001 diff --git a/httpdocs/themes/vuexy/vendor/css/pages/app-chat.css b/httpdocs/themes/vuexy/vendor/css/pages/app-chat.css index 5fa07556..647e1e2e 100755 --- a/httpdocs/themes/vuexy/vendor/css/pages/app-chat.css +++ b/httpdocs/themes/vuexy/vendor/css/pages/app-chat.css @@ -57,6 +57,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;