fix CRM contacts

This commit is contained in:
root 2026-01-18 19:19:03 +03:00
parent 77f76c8c28
commit 308c684aa2
6 changed files with 122 additions and 655 deletions

View File

@ -51,9 +51,14 @@ class TwigGlobalsExtension extends AbstractExtension
// Module subscription functions // Module subscription functions
new TwigFunction('is_module_active', [$this, 'isModuleActive'], ['is_safe' => ['html']]), new TwigFunction('is_module_active', [$this, 'isModuleActive'], ['is_safe' => ['html']]),
new TwigFunction('is_module_available', [$this, 'isModuleAvailable'], ['is_safe' => ['html']]), new TwigFunction('is_module_available', [$this, 'isModuleAvailable'], ['is_safe' => ['html']]),
new TwigFunction('csrf_meta', [$this, 'csrf_meta'], ['is_safe' => ['html']]),
]; ];
} }
public function csrf_meta()
{
return csrf_meta();
}
// ======================================== // ========================================
// Access Functions для Twig // Access Functions для Twig
// ======================================== // ========================================

View File

@ -249,11 +249,14 @@ class ContactsController extends BaseController
]; ];
}, $contacts); }, $contacts);
return $this->response->setJSON([ return $this->response
'success' => true, ->setHeader('X-CSRF-TOKEN', csrf_hash())
'items' => $items, ->setHeader('X-CSRF-HASH', csrf_token())
'total' => count($items), ->setJSON([
]); 'success' => true,
'items' => $items,
'total' => count($items),
]);
} }
/** /**
@ -263,7 +266,12 @@ class ContactsController extends BaseController
public function ajaxStore() public function ajaxStore()
{ {
$organizationId = $this->requireActiveOrg(); $organizationId = $this->requireActiveOrg();
$customerId = $this->request->getPost('customer_id');
// Поддержка JSON и form-urlencoded данных
$jsonData = $this->request->getJSON(true);
$rawInput = $jsonData ?? $this->request->getPost();
$customerId = $rawInput['customer_id'] ?? null;
// Проверяем клиента если указан // Проверяем клиента если указан
if ($customerId) { if ($customerId) {
@ -279,10 +287,10 @@ class ContactsController extends BaseController
$data = [ $data = [
'organization_id' => $organizationId, 'organization_id' => $organizationId,
'customer_id' => $customerId ?: null, 'customer_id' => $customerId ?: null,
'name' => $this->request->getPost('name'), 'name' => $rawInput['name'] ?? '',
'email' => $this->request->getPost('email') ?: null, 'email' => $rawInput['email'] ?? null,
'phone' => $this->request->getPost('phone') ?: null, 'phone' => $rawInput['phone'] ?? null,
'position' => $this->request->getPost('position') ?: null, 'position' => $rawInput['position'] ?? null,
]; ];
// Валидация // Валидация
@ -304,17 +312,20 @@ class ContactsController extends BaseController
])->setStatusCode(422); ])->setStatusCode(422);
} }
return $this->response->setJSON([ return $this->response
'success' => true, ->setHeader('X-CSRF-TOKEN', csrf_hash())
'message' => 'Контакт создан', ->setHeader('X-CSRF-HASH', csrf_token())
'item' => [ ->setJSON([
'id' => $contactId, 'success' => true,
'name' => $data['name'], 'message' => 'Контакт создан',
'email' => $data['email'], 'item' => [
'phone' => $data['phone'], 'id' => $contactId,
'position' => $data['position'], 'name' => $data['name'],
], 'email' => $data['email'],
]); 'phone' => $data['phone'],
'position' => $data['position'],
],
]);
} }
/** /**
@ -334,11 +345,15 @@ class ContactsController extends BaseController
])->setStatusCode(404); ])->setStatusCode(404);
} }
// Поддержка JSON и form-urlencoded данных
$jsonData = $this->request->getJSON(true);
$rawInput = $jsonData ?? $this->request->getPost();
$data = [ $data = [
'name' => $this->request->getPost('name'), 'name' => $rawInput['name'] ?? '',
'email' => $this->request->getPost('email') ?: null, 'email' => $rawInput['email'] ?? null,
'phone' => $this->request->getPost('phone') ?: null, 'phone' => $rawInput['phone'] ?? null,
'position' => $this->request->getPost('position') ?: null, 'position' => $rawInput['position'] ?? null,
]; ];
// Валидация // Валидация
@ -360,17 +375,20 @@ class ContactsController extends BaseController
])->setStatusCode(422); ])->setStatusCode(422);
} }
return $this->response->setJSON([ return $this->response
'success' => true, ->setHeader('X-CSRF-TOKEN', csrf_hash())
'message' => 'Контакт обновлён', ->setHeader('X-CSRF-HASH', csrf_token())
'item' => [ ->setJSON([
'id' => $id, 'success' => true,
'name' => $data['name'], 'message' => 'Контакт обновлён',
'email' => $data['email'], 'item' => [
'phone' => $data['phone'], 'id' => $id,
'position' => $data['position'], 'name' => $data['name'],
], 'email' => $data['email'],
]); 'phone' => $data['phone'],
'position' => $data['position'],
],
]);
} }
/** /**
@ -392,9 +410,12 @@ class ContactsController extends BaseController
$this->contactModel->delete($id); $this->contactModel->delete($id);
return $this->response->setJSON([ return $this->response
'success' => true, ->setHeader('X-CSRF-TOKEN', csrf_hash())
'message' => 'Контакт удалён', ->setHeader('X-CSRF-HASH', csrf_token())
]); ->setJSON([
'success' => true,
'message' => 'Контакт удалён',
]);
} }
} }

View File

@ -7,6 +7,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
{{ csrf_meta() }}
<title>{% block title %}Бизнес.Точка{% endblock %}</title> <title>{% block title %}Бизнес.Точка{% endblock %}</title>
<!-- Bootstrap CSS --> <!-- Bootstrap CSS -->
@ -17,7 +18,7 @@
<link href="{{ base_url('assets/css/base.css') }}" rel="stylesheet"> <link href="{{ base_url('assets/css/base.css') }}" rel="stylesheet">
{% block styles %}{% endblock %} {% block styles %}{% endblock %}
</head> </head>
<body class="bg-light"> <body class="bg-light" data-base-url="{{ base_url('/') }}">
<div class="d-flex" id="wrapper"> <div class="d-flex" id="wrapper">
<!-- SIDEBAR --> <!-- SIDEBAR -->

587
dump.sql
View File

@ -1,587 +0,0 @@
/*M!999999\- enable the sandbox mode */
-- MariaDB dump 10.19-11.8.3-MariaDB, for debian-linux-gnu (x86_64)
--
-- Host: localhost Database: bp_mirv_db
-- ------------------------------------------------------
-- Server version 11.8.3-MariaDB-0+deb13u1 from Debian
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */;
--
-- Table structure for table `ci_sessions`
--
DROP TABLE IF EXISTS `ci_sessions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `ci_sessions` (
`id` varchar(128) NOT NULL,
`ip_address` varchar(45) NOT NULL,
`timestamp` int(10) unsigned NOT NULL DEFAULT 0,
`data` blob DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `timestamp` (`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `ci_sessions`
--
LOCK TABLES `ci_sessions` WRITE;
/*!40000 ALTER TABLE `ci_sessions` DISABLE KEYS */;
set autocommit=0;
/*!40000 ALTER TABLE `ci_sessions` ENABLE KEYS */;
UNLOCK TABLES;
commit;
--
-- Table structure for table `contacts`
--
DROP TABLE IF EXISTS `contacts`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `contacts` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`organization_id` int(11) unsigned NOT NULL,
`customer_id` int(11) unsigned NOT NULL COMMENT 'Ссылка на клиента (компанию)',
`name` varchar(255) NOT NULL COMMENT 'Имя контакта',
`email` varchar(255) DEFAULT NULL,
`phone` varchar(50) DEFAULT NULL,
`position` varchar(255) DEFAULT NULL COMMENT 'Должность',
`is_primary` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 'Основной контакт',
`notes` text DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `organization_id` (`organization_id`),
KEY `customer_id` (`customer_id`),
CONSTRAINT `contacts_customer_id_foreign` FOREIGN KEY (`customer_id`) REFERENCES `organizations_clients` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `contacts_organization_id_foreign` FOREIGN KEY (`organization_id`) REFERENCES `organizations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `contacts`
--
LOCK TABLES `contacts` WRITE;
/*!40000 ALTER TABLE `contacts` DISABLE KEYS */;
set autocommit=0;
INSERT INTO `contacts` VALUES
(1,12,1,'Петров',NULL,'+79999999955','Вахтер',0,NULL,'2026-01-15 02:50:50','2026-01-15 02:50:50',NULL);
/*!40000 ALTER TABLE `contacts` ENABLE KEYS */;
UNLOCK TABLES;
commit;
--
-- Table structure for table `deal_history`
--
DROP TABLE IF EXISTS `deal_history`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `deal_history` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`deal_id` bigint(20) unsigned NOT NULL,
`user_id` int(11) unsigned NOT NULL,
`action` varchar(50) NOT NULL,
`field_name` varchar(50) DEFAULT NULL,
`old_value` text DEFAULT NULL,
`new_value` text DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `deal_id` (`deal_id`),
KEY `user_id` (`user_id`),
CONSTRAINT `deal_history_deal_id_foreign` FOREIGN KEY (`deal_id`) REFERENCES `deals` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `deal_history_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `deal_history`
--
LOCK TABLES `deal_history` WRITE;
/*!40000 ALTER TABLE `deal_history` DISABLE KEYS */;
set autocommit=0;
/*!40000 ALTER TABLE `deal_history` ENABLE KEYS */;
UNLOCK TABLES;
commit;
--
-- Table structure for table `deal_stages`
--
DROP TABLE IF EXISTS `deal_stages`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `deal_stages` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`organization_id` int(11) unsigned NOT NULL,
`name` varchar(255) NOT NULL,
`color` varchar(7) NOT NULL DEFAULT '#6B7280',
`order_index` int(11) NOT NULL DEFAULT 0,
`type` enum('progress','won','lost') NOT NULL DEFAULT 'progress',
`probability` int(3) unsigned NOT NULL DEFAULT 0,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `organization_id` (`organization_id`),
CONSTRAINT `deal_stages_organization_id_foreign` FOREIGN KEY (`organization_id`) REFERENCES `organizations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `deal_stages`
--
LOCK TABLES `deal_stages` WRITE;
/*!40000 ALTER TABLE `deal_stages` DISABLE KEYS */;
set autocommit=0;
INSERT INTO `deal_stages` VALUES
(1,12,'Первый контакт','#6b7280',1,'progress',1,NULL,NULL,NULL),
(2,12,'Второй контакт','#9a9996',2,'progress',5,NULL,NULL,NULL),
(3,12,'Переговоры по договору','#c061cb',3,'progress',10,NULL,NULL,NULL);
/*!40000 ALTER TABLE `deal_stages` ENABLE KEYS */;
UNLOCK TABLES;
commit;
--
-- Table structure for table `deals`
--
DROP TABLE IF EXISTS `deals`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `deals` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`organization_id` int(11) unsigned NOT NULL,
`contact_id` int(11) unsigned DEFAULT NULL,
`company_id` int(11) unsigned DEFAULT NULL,
`title` varchar(255) NOT NULL,
`description` text DEFAULT NULL,
`amount` decimal(15,2) NOT NULL DEFAULT 0.00,
`currency` char(3) NOT NULL DEFAULT 'RUB',
`stage_id` int(11) unsigned NOT NULL,
`assigned_user_id` int(11) unsigned DEFAULT NULL,
`expected_close_date` date DEFAULT NULL,
`created_by` int(11) unsigned NOT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `organization_id` (`organization_id`),
KEY `stage_id` (`stage_id`),
KEY `assigned_user_id` (`assigned_user_id`),
CONSTRAINT `deals_assigned_user_id_foreign` FOREIGN KEY (`assigned_user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
CONSTRAINT `deals_organization_id_foreign` FOREIGN KEY (`organization_id`) REFERENCES `organizations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `deals_stage_id_foreign` FOREIGN KEY (`stage_id`) REFERENCES `deal_stages` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `deals`
--
LOCK TABLES `deals` WRITE;
/*!40000 ALTER TABLE `deals` DISABLE KEYS */;
set autocommit=0;
INSERT INTO `deals` VALUES
(1,12,1,1,'Раскрутка сайта','',1000.00,'RUB',3,NULL,NULL,11,'2026-01-15 03:07:32','2026-01-15 03:07:32',NULL),
(2,12,NULL,1,'Пупупу','',15000.00,'RUB',1,11,'2026-02-28',11,'2026-01-15 03:45:08','2026-01-15 07:43:07',NULL);
/*!40000 ALTER TABLE `deals` ENABLE KEYS */;
UNLOCK TABLES;
commit;
--
-- Table structure for table `migrations`
--
DROP TABLE IF EXISTS `migrations`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `migrations` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`version` varchar(255) NOT NULL,
`class` varchar(255) NOT NULL,
`group` varchar(255) NOT NULL,
`namespace` varchar(255) NOT NULL,
`time` int(11) NOT NULL,
`batch` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `migrations`
--
LOCK TABLES `migrations` WRITE;
/*!40000 ALTER TABLE `migrations` DISABLE KEYS */;
set autocommit=0;
INSERT INTO `migrations` VALUES
(1,'2026-01-07-053357','App\\Database\\Migrations\\CreateUsersTable','default','App',1767769689,1),
(2,'2026-01-07-053401','App\\Database\\Migrations\\CreateOrganizationsTable','default','App',1767769689,1),
(3,'2026-01-07-053407','App\\Database\\Migrations\\CreateOrganizationUsersTable','default','App',1767769689,1),
(4,'2026-01-07-053412','App\\Database\\Migrations\\CreateOrganizationSubscriptionsTable','default','App',1767769689,1),
(5,'2026-01-07-053413','App\\Database\\Migrations\\CreateOrganizationSubscriptionsTable','default','App',1767769974,2),
(6,'2026-01-08-000001','App\\Database\\Migrations\\AddEmailVerificationToUsers','default','App',1767870811,3),
(7,'2026-01-08-200001','App\\Database\\Migrations\\CreateOrganizationsClientsTable','default','App',1767878430,4),
(8,'2026-01-12-000001','App\\Database\\Migrations\\AddInviteFieldsToOrganizationUsers','default','App',1768267451,5),
(9,'2026-01-13-000001','App\\Database\\Migrations\\CreateRememberTokensTable','default','App',1768267451,5),
(10,'2026-01-13-163701','App\\Database\\Migrations\\AddTrialEndsAtToSubscriptions','default','App',1768295204,6),
(11,'2026-01-13-200001','App\\Database\\Migrations\\CreateCiSessionsTable','default','App',1768313545,7),
(12,'2026-01-13-200002','App\\Database\\Migrations\\AddPasswordResetFieldsToUsers','default','App',1768313545,7),
(13,'2026-01-14-000001','App\\Database\\Migrations\\AddSystemRoleToUsers','default','App',1768317531,8),
(14,'2026-01-15-000001','App\\Database\\Migrations\\AddPlansTable','default','App',1768317531,8),
(15,'2026-01-15-000002','App\\Database\\Migrations\\CreateOrganizationPlanSubscriptionsTable','default','App',1768320790,9),
(16,'2026-01-15-000003','App\\Database\\Migrations\\AddTokenExpiresToUsers','default','App',1768372597,10),
(17,'2026-01-15-000004','App\\Database\\Migrations\\AddInviteExpiresToOrganizationUsers','default','App',1768372597,10),
(18,'2026-01-15-000005','App\\Database\\Migrations\\AddStatusToOrganizations','default','App',1768376017,11),
(19,'2026-01-15-000006','App\\Database\\Migrations\\CreateDealsTables','default','App',1768439721,12),
(21,'2026-01-15-000007','App\\Database\\Migrations\\CreateContactsTable','default','App',1768440786,13),
(22,'2026-01-16-210001','App\\Database\\Migrations\\DropOrganizationPlanSubscriptionsTable','default','App',1768572350,14),
(23,'2026-01-16-220001','App\\Database\\Migrations\\CreateModuleSettingsTable','default','App',1768573190,15);
/*!40000 ALTER TABLE `migrations` ENABLE KEYS */;
UNLOCK TABLES;
commit;
--
-- Table structure for table `module_settings`
--
DROP TABLE IF EXISTS `module_settings`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `module_settings` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`module_code` varchar(50) NOT NULL,
`name` varchar(100) NOT NULL,
`description` varchar(255) NOT NULL,
`price_monthly` int(11) NOT NULL DEFAULT 0,
`price_yearly` int(11) NOT NULL DEFAULT 0,
`trial_days` int(11) NOT NULL DEFAULT 0,
`is_active` tinyint(1) NOT NULL DEFAULT 1,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `module_code` (`module_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `module_settings`
--
LOCK TABLES `module_settings` WRITE;
/*!40000 ALTER TABLE `module_settings` DISABLE KEYS */;
set autocommit=0;
/*!40000 ALTER TABLE `module_settings` ENABLE KEYS */;
UNLOCK TABLES;
commit;
--
-- Table structure for table `organization_subscriptions`
--
DROP TABLE IF EXISTS `organization_subscriptions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `organization_subscriptions` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`organization_id` int(11) unsigned NOT NULL,
`module_code` varchar(50) NOT NULL,
`status` enum('trial','active','expired','cancelled') NOT NULL DEFAULT 'trial',
`trial_ends_at` datetime DEFAULT NULL COMMENT 'Дата окончания триального периода',
`expires_at` datetime DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `organization_id_module_code` (`organization_id`,`module_code`),
CONSTRAINT `organization_subscriptions_organization_id_foreign` FOREIGN KEY (`organization_id`) REFERENCES `organizations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `organization_subscriptions`
--
LOCK TABLES `organization_subscriptions` WRITE;
/*!40000 ALTER TABLE `organization_subscriptions` DISABLE KEYS */;
set autocommit=0;
/*!40000 ALTER TABLE `organization_subscriptions` ENABLE KEYS */;
UNLOCK TABLES;
commit;
--
-- Table structure for table `organization_users`
--
DROP TABLE IF EXISTS `organization_users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `organization_users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`organization_id` int(11) unsigned NOT NULL,
`user_id` int(11) unsigned NOT NULL,
`role` enum('owner','admin','manager','guest') NOT NULL DEFAULT 'manager',
`invite_token` varchar(64) DEFAULT NULL,
`invited_by` int(10) unsigned DEFAULT NULL,
`invited_at` datetime DEFAULT NULL,
`invite_expires_at` datetime DEFAULT NULL,
`status` enum('active','pending','invited','blocked') NOT NULL DEFAULT 'pending',
`joined_at` datetime DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `organization_id_user_id` (`organization_id`,`user_id`),
KEY `organization_users_user_id_foreign` (`user_id`),
KEY `idx_org_users_token` (`invite_token`),
CONSTRAINT `organization_users_organization_id_foreign` FOREIGN KEY (`organization_id`) REFERENCES `organizations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `organization_users_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `organization_users`
--
LOCK TABLES `organization_users` WRITE;
/*!40000 ALTER TABLE `organization_users` DISABLE KEYS */;
set autocommit=0;
INSERT INTO `organization_users` VALUES
(12,12,11,'owner',NULL,NULL,NULL,NULL,'active','2026-01-08 12:48:27',NULL),
(13,13,11,'owner',NULL,NULL,NULL,NULL,'active','2026-01-08 15:29:08',NULL);
/*!40000 ALTER TABLE `organization_users` ENABLE KEYS */;
UNLOCK TABLES;
commit;
--
-- Table structure for table `organizations`
--
DROP TABLE IF EXISTS `organizations`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `organizations` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`owner_id` int(11) unsigned NOT NULL,
`name` varchar(255) NOT NULL,
`type` enum('business','personal') NOT NULL DEFAULT 'business',
`logo` varchar(255) DEFAULT NULL,
`requisites` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`requisites`)),
`trial_ends_at` datetime DEFAULT NULL,
`settings` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`settings`)),
`status` enum('active','blocked') NOT NULL DEFAULT 'active',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `organizations_owner_id_foreign` (`owner_id`),
CONSTRAINT `organizations_owner_id_foreign` FOREIGN KEY (`owner_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `organizations`
--
LOCK TABLES `organizations` WRITE;
/*!40000 ALTER TABLE `organizations` DISABLE KEYS */;
set autocommit=0;
INSERT INTO `organizations` VALUES
(12,11,'Личное пространство','personal',NULL,NULL,NULL,NULL,'active','2026-01-08 12:48:27','2026-01-08 12:48:27',NULL),
(13,11,'Редька','business',NULL,'{\"inn\":\"1112223334\",\"ogrn\":\"1231231231230\",\"kpp\":\"\",\"legal_address\":\"\\u041f\\u0438\\u0442\\u0435\\u0440, \\u041c\\u043e\\u0439\\u043a\\u0430 13\",\"actual_address\":\"\",\"phone\":\"\",\"email\":\"\",\"website\":\"\",\"bank_name\":\"\",\"bank_bik\":\"\",\"checking_account\":\"\",\"correspondent_account\":\"\"}',NULL,'[]','active','2026-01-08 15:29:08','2026-01-14 07:34:02',NULL);
/*!40000 ALTER TABLE `organizations` ENABLE KEYS */;
UNLOCK TABLES;
commit;
--
-- Table structure for table `organizations_clients`
--
DROP TABLE IF EXISTS `organizations_clients`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `organizations_clients` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`organization_id` int(11) unsigned NOT NULL,
`name` varchar(255) NOT NULL,
`email` varchar(255) DEFAULT NULL,
`phone` varchar(50) DEFAULT NULL,
`notes` text DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `organization_id` (`organization_id`),
CONSTRAINT `organizations_clients_organization_id_foreign` FOREIGN KEY (`organization_id`) REFERENCES `organizations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `organizations_clients`
--
LOCK TABLES `organizations_clients` WRITE;
/*!40000 ALTER TABLE `organizations_clients` DISABLE KEYS */;
set autocommit=0;
INSERT INTO `organizations_clients` VALUES
(1,12,'РогаКопыта','','','Текст','2026-01-08 13:40:47','2026-01-11 15:52:48',NULL),
(2,12,'Третий','ttt@ttt.com','','','2026-01-08 14:15:49','2026-01-08 14:15:49',NULL),
(3,12,'ыва','ddd@ddd.com','','','2026-01-08 21:34:38','2026-01-08 21:34:38',NULL),
(4,12,'ппп','','','','2026-01-12 01:22:38','2026-01-12 01:22:38',NULL),
(5,12,'ккк','','','','2026-01-12 01:22:43','2026-01-12 01:22:43',NULL),
(6,12,'еее','','','','2026-01-12 01:22:49','2026-01-12 01:22:49',NULL),
(7,12,'ннн','','','','2026-01-12 01:22:53','2026-01-12 01:22:53',NULL),
(8,12,'ггг','test3@test.com','','Вот такие вот заметки ','2026-01-12 01:22:56','2026-01-15 03:51:00',NULL),
(9,12,'шшш','','','','2026-01-12 01:22:59','2026-01-12 01:22:59',NULL),
(10,12,'щщщ','','','','2026-01-12 01:23:04','2026-01-12 01:23:04',NULL),
(11,12,'ффф','','','','2026-01-12 01:23:08','2026-01-12 01:23:08',NULL),
(12,13,'Супер','','','','2026-01-12 02:56:33','2026-01-12 02:56:33',NULL),
(13,13,'Супер222','','','','2026-01-12 09:04:04','2026-01-12 09:04:16',NULL);
/*!40000 ALTER TABLE `organizations_clients` ENABLE KEYS */;
UNLOCK TABLES;
commit;
--
-- Table structure for table `plans`
--
DROP TABLE IF EXISTS `plans`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `plans` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`description` text DEFAULT NULL,
`price` decimal(10,2) NOT NULL DEFAULT 0.00,
`currency` varchar(3) NOT NULL DEFAULT 'RUB',
`billing_period` enum('monthly','yearly','quarterly') NOT NULL DEFAULT 'monthly',
`max_users` int(11) NOT NULL DEFAULT 5,
`max_clients` int(11) NOT NULL DEFAULT 100,
`max_storage` int(11) NOT NULL DEFAULT 10,
`features` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`features`)),
`is_active` tinyint(4) NOT NULL DEFAULT 1,
`is_default` tinyint(4) NOT NULL DEFAULT 0,
`created_at` datetime NOT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `plans`
--
LOCK TABLES `plans` WRITE;
/*!40000 ALTER TABLE `plans` DISABLE KEYS */;
set autocommit=0;
INSERT INTO `plans` VALUES
(1,'Бесплатный','Базовый тариф для небольших команд',0.00,'RUB','monthly',3,50,5,'[\"\\u0411\\u0430\\u0437\\u043e\\u0432\\u044b\\u0435 \\u043c\\u043e\\u0434\\u0443\\u043b\\u0438\",\"Email \\u043f\\u043e\\u0434\\u0434\\u0435\\u0440\\u0436\\u043a\\u0430\",\"\\u042d\\u043a\\u0441\\u043f\\u043e\\u0440\\u0442 \\u0432 CSV\"]',1,1,'2026-01-13 15:18:51',NULL),
(2,'Старт','Тариф для растущих компаний',990.00,'RUB','monthly',10,500,50,'[\"\\u0412\\u0441\\u0435 \\u043c\\u043e\\u0434\\u0443\\u043b\\u0438\",\"\\u041f\\u0440\\u0438\\u043e\\u0440\\u0438\\u0442\\u0435\\u0442\\u043d\\u0430\\u044f \\u043f\\u043e\\u0434\\u0434\\u0435\\u0440\\u0436\\u043a\\u0430\",\"\\u042d\\u043a\\u0441\\u043f\\u043e\\u0440\\u0442 \\u0432 PDF \\u0438 Excel\",\"API \\u0434\\u043e\\u0441\\u0442\\u0443\\u043f\"]',1,0,'2026-01-13 15:18:51',NULL),
(3,'Бизнес','Полный функционал для крупных компаний',4990.00,'RUB','monthly',50,5000,500,'[\"\\u0412\\u0441\\u0435 \\u043c\\u043e\\u0434\\u0443\\u043b\\u0438\",\"\\u041f\\u0435\\u0440\\u0441\\u043e\\u043d\\u0430\\u043b\\u044c\\u043d\\u044b\\u0439 \\u043c\\u0435\\u043d\\u0435\\u0434\\u0436\\u0435\\u0440\",\"\\u042d\\u043a\\u0441\\u043f\\u043e\\u0440\\u0442 \\u0432 PDF \\u0438 Excel\",\"\\u041f\\u043e\\u043b\\u043d\\u044b\\u0439 API \\u0434\\u043e\\u0441\\u0442\\u0443\\u043f\",\"\\u0418\\u043d\\u0442\\u0435\\u0433\\u0440\\u0430\\u0446\\u0438\\u0438\",\"\\u0411\\u0440\\u0435\\u043d\\u0434\\u0438\\u043d\\u0433\"]',1,0,'2026-01-13 15:18:51',NULL);
/*!40000 ALTER TABLE `plans` ENABLE KEYS */;
UNLOCK TABLES;
commit;
--
-- Table structure for table `remember_tokens`
--
DROP TABLE IF EXISTS `remember_tokens`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `remember_tokens` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) unsigned NOT NULL,
`selector` varchar(64) NOT NULL,
`token_hash` varchar(128) NOT NULL,
`expires_at` datetime NOT NULL,
`created_at` datetime DEFAULT NULL,
`user_agent` varchar(500) DEFAULT NULL,
`ip_address` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `selector` (`selector`),
KEY `expires_at` (`expires_at`),
CONSTRAINT `remember_tokens_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `remember_tokens`
--
LOCK TABLES `remember_tokens` WRITE;
/*!40000 ALTER TABLE `remember_tokens` DISABLE KEYS */;
set autocommit=0;
INSERT INTO `remember_tokens` VALUES
(4,11,'508781e7a1d7bfb7b15c4ed38a71e205','dc94c9825eed3efd9d960fdefb9e9048203a2b837d8168e1c78ce1cc3e38f90d','2026-02-13 07:17:58','2026-01-14 07:17:58','Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0','91.234.172.241');
/*!40000 ALTER TABLE `remember_tokens` ENABLE KEYS */;
UNLOCK TABLES;
commit;
--
-- Table structure for table `users`
--
DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`email` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`system_role` enum('user','admin','superadmin') DEFAULT 'user',
`name` varchar(100) NOT NULL,
`phone` varchar(20) DEFAULT NULL,
`avatar` varchar(255) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`verification_token` varchar(255) DEFAULT NULL COMMENT 'Токен для подтверждения email',
`token_expires_at` datetime DEFAULT NULL,
`email_verified` tinyint(1) DEFAULT 0 COMMENT 'Статус подтверждения email (0 - не подтвержден, 1 - подтвержден)',
`verified_at` datetime DEFAULT NULL COMMENT 'Дата и время подтверждения email',
`reset_token` varchar(255) DEFAULT NULL,
`reset_expires_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `users`
--
LOCK TABLES `users` WRITE;
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
set autocommit=0;
INSERT INTO `users` VALUES
(11,'mirvtop@yandex.ru','$2y$12$lPkp/tIPEgltYiyIw5ZWuukNPrMdGzfzerRG1oSXbKuBSpVbBmhhu','superadmin','Mirivlad',NULL,'avatar_11_1768273671.jpg','2026-01-08 12:48:27','2026-01-13 15:29:21',NULL,NULL,1,'2026-01-08 12:48:38',NULL,NULL);
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
UNLOCK TABLES;
commit;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */;
-- Dump completed on 2026-01-16 17:43:45

View File

@ -5,7 +5,7 @@ document.addEventListener("DOMContentLoaded", function () {
const body = document.body; const body = document.body;
// Базовая функция для получения base URL (объявляем ДО использования) // Базовая функция для получения base URL (объявляем ДО использования)
const baseUrl = '{{ base_url("/") }}'.replace(/\/+$/, ''); const baseUrl = '/'.replace(/\/+$/, '');
if (sidebarToggle) { if (sidebarToggle) {
// Обработчик клика // Обработчик клика

View File

@ -24,6 +24,16 @@ class ContactsManager {
this.loadContacts(); this.loadContacts();
} }
/**
* Получить заголовки запроса
*/
getHeaders() {
return {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
};
}
/** /**
* Загрузить список контактов * Загрузить список контактов
*/ */
@ -31,9 +41,8 @@ class ContactsManager {
try { try {
const response = await fetch(`${this.apiUrl}/list/${this.clientId}`, { const response = await fetch(`${this.apiUrl}/list/${this.clientId}`, {
method: 'POST', method: 'POST',
headers: { credentials: 'same-origin',
'Content-Type': 'application/json', headers: this.getHeaders(),
},
body: JSON.stringify({}) body: JSON.stringify({})
}); });
@ -106,6 +115,7 @@ class ContactsManager {
* Отобразить одну строку контакта * Отобразить одну строку контакта
*/ */
renderRow(contact) { renderRow(contact) {
const escapedId = this.escapeJs(contact.id);
return ` return `
<tr data-id="${contact.id}" class="contact-row"> <tr data-id="${contact.id}" class="contact-row">
<td> <td>
@ -129,29 +139,27 @@ class ContactsManager {
value="${this.escapeHtml(contact.position || '')}" style="display: none;" placeholder="Должность"> value="${this.escapeHtml(contact.position || '')}" style="display: none;" placeholder="Должность">
</td> </td>
<td class="text-end"> <td class="text-end">
<div class="contact-actions"> <div class="btn-group contact-actions">
<button type="button" class="btn btn-outline-primary btn-sm" <button type="button" class="btn btn-outline-primary btn-sm"
onclick="contactsManager.edit(${contact.id})" title="Редактировать"> onclick="contactsManager.edit('${escapedId}')" title="Редактировать">
<i class="fa-solid fa-pen"></i> <i class="fa-solid fa-pen"></i>
</button> </button>
<button type="button" class="btn btn-outline-danger btn-sm" <button type="button" class="btn btn-outline-danger btn-sm"
onclick="contactsManager.remove(${contact.id})" title="Удалить"> onclick="contactsManager.remove('${escapedId}')" title="Удалить">
<i class="fa-solid fa-trash"></i> <i class="fa-solid fa-trash"></i>
</button> </button>
</div> </div>
<div class="edit-actions" style="display: none;"> <div class="btn-group edit-actions" style="display: none;">
<button type="button" class="btn btn-outline-success btn-sm" <button type="button" class="btn btn-outline-success btn-sm"
onclick="contactsManager.save(${contact.id})" title="Сохранить"> onclick="contactsManager.save('${escapedId}')" title="Сохранить">
<i class="fa-solid fa-check"></i> <i class="fa-solid fa-check"></i>
</button> </button>
<button type="button" class="btn btn-outline-secondary btn-sm" <button type="button" class="btn btn-outline-secondary btn-sm"
onclick="contactsManager.cancel(${contact.id})" title="Отмена"> onclick="contactsManager.cancel('${escapedId}')" title="Отмена">
<i class="fa-solid fa-times"></i> <i class="fa-solid fa-times"></i>
</button> </button>
</div> </div>
</td> </td>`;
</tr>
`;
} }
/** /**
@ -225,18 +233,16 @@ class ContactsManager {
// Создание нового // Создание нового
response = await fetch(`${this.apiUrl}/store`, { response = await fetch(`${this.apiUrl}/store`, {
method: 'POST', method: 'POST',
headers: { credentials: 'same-origin',
'Content-Type': 'application/json', headers: this.getHeaders(),
},
body: JSON.stringify(data) body: JSON.stringify(data)
}); });
} else { } else {
// Обновление существующего // Обновление существующего
response = await fetch(`${this.apiUrl}/update/${contactId}`, { response = await fetch(`${this.apiUrl}/update/${contactId}`, {
method: 'POST', method: 'POST',
headers: { credentials: 'same-origin',
'Content-Type': 'application/json', headers: this.getHeaders(),
},
body: JSON.stringify(data) body: JSON.stringify(data)
}); });
} }
@ -245,13 +251,15 @@ class ContactsManager {
if (result.success) { if (result.success) {
// Обновляем локальный массив // Обновляем локальный массив
const index = this.contacts.findIndex(c => c.id === contactId); // contactId может быть строкой из data-id, а c.id - числом из БД
const contactIdStr = String(contactId);
const index = this.contacts.findIndex(c => String(c.id) === contactIdStr);
if (index !== -1) { if (index !== -1) {
if (result.item) { if (result.item) {
// Обновляем с реальным ID от сервера // Обновляем с реальным ID от сервера
this.contacts[index] = { ...data, id: result.item.id }; this.contacts[index] = { ...data, id: result.item.id };
} else { } else {
this.contacts[index] = data; this.contacts[index] = { ...data, id: contactId };
} }
} }
@ -270,9 +278,10 @@ class ContactsManager {
* Отменить редактирование * Отменить редактирование
*/ */
cancel(contactId) { cancel(contactId) {
if (contactId.toString().startsWith('new_')) { const contactIdStr = String(contactId);
if (contactIdStr.startsWith('new_')) {
// Удаляем новую строку // Удаляем новую строку
this.contacts = this.contacts.filter(c => c.id !== contactId); this.contacts = this.contacts.filter(c => String(c.id) !== contactIdStr);
this.render(); this.render();
} else { } else {
// Перезагружаем данные // Перезагружаем данные
@ -291,16 +300,17 @@ class ContactsManager {
try { try {
const response = await fetch(`${this.apiUrl}/delete/${contactId}`, { const response = await fetch(`${this.apiUrl}/delete/${contactId}`, {
method: 'POST', method: 'POST',
headers: { credentials: 'same-origin',
'Content-Type': 'application/json', headers: this.getHeaders(),
},
body: JSON.stringify({}) body: JSON.stringify({})
}); });
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
this.contacts = this.contacts.filter(c => c.id !== contactId); // contactId может быть строкой, а c.id - числом
const contactIdStr = String(contactId);
this.contacts = this.contacts.filter(c => String(c.id) !== contactIdStr);
this.render(); this.render();
this.showSuccess(result.message || 'Контакт удалён'); this.showSuccess(result.message || 'Контакт удалён');
} else { } else {
@ -361,6 +371,23 @@ class ContactsManager {
div.textContent = text; div.textContent = text;
return div.innerHTML; return div.innerHTML;
} }
/**
* Экранирование для JavaScript строки
*/
escapeJs(text) {
if (!text) return '';
// Приводим к строке, так как id может быть числом
const str = String(text);
return str
.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/</g, '\\x3c')
.replace(/>/g, '\\x3e');
}
} }
// Инициализация при загрузке страницы // Инициализация при загрузке страницы