From 5fa4abba5f682bbc342a4f45ba8f2db5b944d4b5 Mon Sep 17 00:00:00 2001 From: Arsen Mirzaev Tatyano-Muradovich Date: Sat, 12 Oct 2024 19:19:55 +0300 Subject: [PATCH] languages + close button + import fix + total rebuild --- .../arming_bot/system/controllers/catalog.php | 62 +++----- .../arming_bot/system/controllers/core.php | 40 +++-- mirzaev/arming_bot/system/models/account.php | 146 ++++++++++-------- mirzaev/arming_bot/system/models/catalog.php | 109 +++++++------ mirzaev/arming_bot/system/models/core.php | 87 ++++------- mirzaev/arming_bot/system/models/entry.php | 12 +- .../system/models/enumerations/language.php | 45 ++++++ .../system/models/enumerations/session.php | 8 + .../system/models/interfaces/document.php | 3 - mirzaev/arming_bot/system/models/product.php | 75 +++++---- mirzaev/arming_bot/system/models/session.php | 22 +-- mirzaev/arming_bot/system/models/settings.php | 24 ++- .../arming_bot/system/models/suspension.php | 66 ++++---- mirzaev/arming_bot/system/models/telegram.php | 8 +- .../system/models/traits/document.php | 3 +- mirzaev/arming_bot/system/public/index.php | 5 +- .../arming_bot/system/public/js/catalog.js | 122 +++++++++------ .../themes/default/css/catalog/2columns.css | 10 +- .../public/themes/default/css/icons/close.css | 30 ++++ .../system/public/themes/default/css/main.css | 5 +- .../public/themes/default/css/window.css | 23 ++- mirzaev/arming_bot/system/views/templater.php | 49 +++++- .../default/catalog/elements/categories.html | 12 +- .../catalog/elements/products/2columns.html | 10 +- .../views/themes/default/catalog/page.html | 1 + 25 files changed, 581 insertions(+), 396 deletions(-) create mode 100644 mirzaev/arming_bot/system/models/enumerations/language.php create mode 100755 mirzaev/arming_bot/system/public/themes/default/css/icons/close.css diff --git a/mirzaev/arming_bot/system/controllers/catalog.php b/mirzaev/arming_bot/system/controllers/catalog.php index 25f0471..c2e03bd 100755 --- a/mirzaev/arming_bot/system/controllers/catalog.php +++ b/mirzaev/arming_bot/system/controllers/catalog.php @@ -35,11 +35,15 @@ final class catalog extends core */ public function index(array $parameters = []): ?string { - if (!empty($parameters['category'])) { + // Initializing identifier of a category + preg_match('/[\d]+/', $parameters['identifier'] ?? '', $matches); + $identifier = $matches[0] ?? null; + + if (!empty($parameters['identifier'])) { // Передана категория (идентификатор) // Инициализация актуальной категории - $category = new category(document: category::_read('d.identifier == @identifier', parameters: ['identifier' => $parameters['category']])); + $category = category::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $identifier], errors: $this->errors['catalog']); if ($category instanceof category) { // Found the category @@ -84,7 +88,10 @@ final class catalog extends core // Generating filters $this->view->filters = [ - 'brands' => product::collect('d.brand.ru', $this->errors['catalog']) + 'brands' => product::collect( + 'd.brand.' . $this->language->name, + errors: $this->errors['catalog'] + ) ]; if ($_SERVER['REQUEST_METHOD'] === 'GET') { @@ -110,7 +117,7 @@ final class catalog extends core 'html' => [ 'categories' => $this->view->render('catalog/elements/categories.html'), 'products' => $this->view->render('catalog/elements/products/2columns.html'), - 'filters' => $this->view->render('catalog/elemments/filters.html') + 'filters' => $this->view->render('catalog/elements/filters.html') ], 'errors' => $this->errors ] @@ -151,6 +158,7 @@ final class catalog extends core filter: 'd.deleted != true && d.hidden != true', sort: 'd.position ASC, d.name ASC, d.created DESC', amount: 30, + language: $this->language, errors: $this->errors['catalog'] ) : []; @@ -167,7 +175,7 @@ final class catalog extends core [ 'title' => $title ?? '', 'html' => [ - 'products' => $this->view->render('catalog/elements/products.html') + 'products' => $this->view->render('catalog/elements/products/2columns.html') ], 'errors' => $this->errors ] @@ -195,53 +203,27 @@ final class catalog extends core */ public function product(array $parameters = []): ?string { - // Initializing of text fore search - preg_match('/[\d]+/', $parameters['id'] ?? '', $matches); - $_key = $matches[0] ?? null; + // Initializing identifier of a product + preg_match('/[\d]+/', $parameters['identifier'] ?? '', $matches); + $identifier = $matches[0] ?? null; - if (!empty($_key)) { - // Received id of prouct (_key) + if (!empty($identifier)) { + // Received identifier of the product if ($_SERVER['REQUEST_METHOD'] === 'POST') { // POST request // Search for products $product = product::read( - filter: "d._key == \"$_key\" && d.deleted != true && d.hidden != true", + filter: "d.identifier == @identifier && d.deleted != true && d.hidden != true", sort: 'd.created DESC', amount: 1, - return: '{id: d._key, title: d.title.ru, description: d.description.ru, cost: d.cost, weight: d.weight, dimensions: d.dimensions, brand: d.brand.ru, compatibility: d.compatibility.ru}', + return: '{identifier: d.identifier, name: d.name.@language, description: d.description.@language, cost: d.cost, weight: d.weight, dimensions: d.dimensions, brand: d.brand.@language, compatibility: d.compatibility.@language, images: d.images[*].storage}', + parameters: ['identifier' => (int) $identifier], + language: $this->language, errors: $this->errors['catalog'] )[0]?->getAll(); - if (!empty($product)) { - // Found the product - - // Initializing buffer of images - $images = []; - - foreach ( - glob(INDEX . - DIRECTORY_SEPARATOR . - 'themes' . - DIRECTORY_SEPARATOR . - (THEME ?? 'default') . - DIRECTORY_SEPARATOR . - 'images' . - DIRECTORY_SEPARATOR . - $_key . - DIRECTORY_SEPARATOR . - "*.{jpg,png,gif}", GLOB_BRACE) as $file - ) { - // Iterate over images of the product - - // Write to buffer of images - $images[] = "/images/$_key/" . basename($file); - } - - $product = $product + ['images' => $images]; - } - // Initializing a response headers header('Content-Type: application/json'); header('Content-Encoding: none'); diff --git a/mirzaev/arming_bot/system/controllers/core.php b/mirzaev/arming_bot/system/controllers/core.php index cb8e006..88b3ce0 100755 --- a/mirzaev/arming_bot/system/controllers/core.php +++ b/mirzaev/arming_bot/system/controllers/core.php @@ -10,7 +10,8 @@ use mirzaev\arming_bot\views\templater, mirzaev\arming_bot\models\account, mirzaev\arming_bot\models\session, mirzaev\arming_bot\models\settings, - mirzaev\arming_bot\models\suspension; + mirzaev\arming_bot\models\suspension, + mirzaev\arming_bot\models\enumerations\language; // Framework for PHP use mirzaev\minimal\controller; @@ -31,7 +32,7 @@ class core extends controller /** * Instance of the settings */ - public static settings $settings; + protected readonly settings $settings; /** * Instance of a session @@ -43,6 +44,11 @@ class core extends controller */ protected readonly ?account $account; + /** + * Language + */ + protected language $language = language::en; + /** * Registry of errors */ @@ -57,6 +63,8 @@ class core extends controller * @param bool $initialize Initialize a controller? * * @return void + * + * @todo settings account и session не имеют проверок на возврат null */ public function __construct(bool $initialize = true) { @@ -70,7 +78,7 @@ class core extends controller // Initializing is requested // Initializing of models core (connect to ArangoDB...) - new models(); + new models(true); // Initializing of the date until which the session will be active $expires = strtotime('+1 week'); @@ -106,10 +114,18 @@ class core extends controller $this->account = $this->session->account($this->errors['account']); // Initializing of the settings - self::$settings = settings::active(); + $this->settings = settings::active(); + + // Initializing of language + if ($this->account?->language) $this->language = $this->account->language ?? language::en; + else if ($this->settings?->language) $this->language = $this->settings->language ?? language::en; // Initializing of preprocessor of views - $this->view = new templater($this->session, $this->account); + $this->view = new templater( + session: $this->session, + account: $this->account, + settings: $this->settings + ); // @todo перенести в middleware @@ -142,20 +158,20 @@ class core extends controller suspension: // Write title of the page to templater global variables - $this->view->title = match ($account?->language ?? self::$settings?->language) { - 'ru' => 'Приостановлено', - 'en' => 'Suspended', + $this->view->title = match ($this->language) { + language::en => 'Suspended', + language::ru => 'Приостановлено', default => 'Suspended' }; // Write description of the suspension to templater global variables - $this->view->description = $suspension->description[$account?->language ?? self::$settings?->language] ?? array_values($suspension->description)[0]; + $this->view->description = $suspension->description[$this->language] ?? array_values($suspension->description)[0]; // Write message of remaining time of the suspension to templater global variables $this->view->remain = [ - 'title' => match ($account?->language ?? self::$settings?->language) { - 'ru' => 'Осталось времени: ', - 'en' => 'Time remaining: ', + 'title' => match ($this->language) { + language::en => 'Time remaining: ', + language::ru => 'Осталось времени: ', default => 'Time remaining: ' }, 'value' => $suspension?->message() diff --git a/mirzaev/arming_bot/system/models/account.php b/mirzaev/arming_bot/system/models/account.php index 47d777c..6eea86c 100755 --- a/mirzaev/arming_bot/system/models/account.php +++ b/mirzaev/arming_bot/system/models/account.php @@ -8,7 +8,8 @@ namespace mirzaev\arming_bot\models; use mirzaev\arming_bot\models\core, mirzaev\arming_bot\models\traits\status, mirzaev\arming_bot\models\traits\document as arangodb_document_trait, - mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface; + mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface, + mirzaev\arming_bot\models\enumerations\language; // Framework for ArangoDB use mirzaev\arangodb\collection, @@ -17,6 +18,9 @@ use mirzaev\arangodb\collection, // Framework for Telegram use Zanzara\Telegram\Type\User as telegram; +// Library for ArangoDB +use ArangoDBClient\Document as _document; + // Built-in libraries use exception; @@ -52,73 +56,87 @@ final class account extends core implements arangodb_document_interface if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) { // Initialized the collection - try { - // Initializing the account and exit (success) - return new static( - document: collection::execute( - <<<'AQL' - FOR d IN @@collection - FILTER d.identifier == @identifier - RETURN d - AQL, + // Initializing the account + $result = collection::execute( + <<<'AQL' + FOR d IN @@collection + FILTER d.identifier == @identifier + RETURN d + AQL, + [ + '@collection' => static::COLLECTION, + 'identifier' => $identifier + ], + errors: $errors + ); + + if ($result instanceof _document) { + // Initialized the account + + // Initializing the object + $account = new account; + + if (method_exists($account, '__document')) { + // Object can implement a document from ArangoDB + + // Abstractioning of parameters + $result->language = language::{$result->language} ?? 'en'; + + // Writing the instance of account document from ArangoDB to the implement object + $account->__document($result); + + // Exit (success) + return $account; + } + } else if ($registration) { + // Not found the account and registration is requested + + // Creating account + $_id = document::write( + static::COLLECTION, + (is_array($registration) + ? $registration : [ - '@collection' => static::COLLECTION, - 'identifier' => $identifier - ], - errors: $errors - ) + 'identifier' => $registration->getId(), + 'name' => [ + 'first' => $registration->getFirstName(), + 'last' => $registration->getLastName() + ], + 'domain' => $registration->getUsername(), + 'robot' => $registration->isBot(), + 'banned' => false, + 'tester' => false, + 'developer' => false, + 'access' => [ + 'settings' => false + ], + 'menus' => [ + 'attachments' => $registration->getAddedToAttachmentMenu() + ], + 'messages' => true, + 'groups' => [ + 'join' => $registration->getCanJoinGroups(), + 'messages' => $registration->getCanReadAllGroupMessages() + ], + 'premium' => $registration->isPremium(), + 'language' => language::{$registration->getLanguageCode()}->name ?? 'en', + 'queries' => [ + 'inline' => $registration->getSupportsInlineQueries() + ] + ]) + [ + 'version' => ROBOT_VERSION, + 'active' => true + ], + errors: $errors ); - } catch (exception $e) { - if ($registration) { - // Not found the account and registration is requested - // Creating account - $account = document::write( - static::COLLECTION, - (is_array($registration) - ? $registration : - [ - 'identifier' => $registration->getId(), - 'name' => [ - 'first' => $registration->getFirstName(), - 'last' => $registration->getLastName() - ], - 'domain' => $registration->getUsername(), - 'robot' => $registration->isBot(), - 'banned' => false, - 'tester' => false, - 'developer' => false, - 'access' => [ - 'settings' => false - ], - 'menus' => [ - 'attachments' => $registration->getAddedToAttachmentMenu() - ], - 'messages' => true, - 'groups' => [ - 'join' => $registration->getCanJoinGroups(), - 'messages' => $registration->getCanReadAllGroupMessages() - ], - 'premium' => $registration->isPremium(), - 'language' => $registration->getLanguageCode(), - 'queries' => [ - 'inline' => $registration->getSupportsInlineQueries() - ] - ]) + [ - 'version' => ROBOT_VERSION, - 'active' => true - ], - errors: $errors - ); + if ($_id) { + // Created account - if ($account) { - // Created account - - // Initializing of the account (without registration request) - return static::initialize($identifier, errors: $errors); - } else throw new exception('Failed to register account'); - } else throw new exception('Failed to find account'); - } + // Initializing of the account (without registration request) + return static::initialize($identifier, errors: $errors); + } else throw new exception('Failed to register account'); + } else throw new exception('Failed to find account'); } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } catch (exception $e) { // Writing to the registry of errors diff --git a/mirzaev/arming_bot/system/models/catalog.php b/mirzaev/arming_bot/system/models/catalog.php index 8fbd210..dd96e54 100755 --- a/mirzaev/arming_bot/system/models/catalog.php +++ b/mirzaev/arming_bot/system/models/catalog.php @@ -10,6 +10,7 @@ use mirzaev\arming_bot\models\core, mirzaev\arming_bot\models\category, mirzaev\arming_bot\models\entry, mirzaev\arming_bot\models\traits\files, + mirzaev\arming_bot\models\enumerations\language, mirzaev\arming_bot\models\traits\yandex\disk as yandex; // Framework for ArangoDB @@ -40,26 +41,26 @@ final class catalog extends core * Collect parameter from all products * * @param string $documment Path to the EXCEL-document - * @param int &$categories_loaded Counter of loaded categories - * @param int &$categories_created Counter of created categories - * @param int &$categories_updated Counter of updated categories - * @param int &$categories_deleted Counter of deleted categories - * @param int &$categories_new Counter of new categories - * @param int &$categories_old Counter of old categories - * @param int &$products_loaded Counter of loaded products - * @param int &$products_created Counter of created products - * @param int &$products_updated Counter of updated products - * @param int &$products_deleted Counter of deleted products - * @param int &$products_new Counter of new products - * @param int &$products_old Counter of old products - * @param string &$language Language (en, ru...) + * @param int &$categories_loaded Counter of loaded categories + * @param int &$categories_created Counter of created categories + * @param int &$categories_updated Counter of updated categories + * @param int &$categories_deleted Counter of deleted categories + * @param int &$categories_new Counter of new categories + * @param int &$categories_old Counter of old categories + * @param int &$products_loaded Counter of loaded products + * @param int &$products_created Counter of created products + * @param int &$products_updated Counter of updated products + * @param int &$products_deleted Counter of deleted products + * @param int &$products_new Counter of new products + * @param int &$products_old Counter of old products + * @param language $language Language * @param array &$errors Registry of errors * * @return void * * @todo * 1. Сначала создать все категории и затем снова по циклу пройтись уже создавать entry между ними - * 2. Сжимать изображения + * 2. Сжимать изображения */ public static function import( string $document, @@ -75,7 +76,7 @@ final class catalog extends core int &$products_deleted = 0, int &$products_old = 0, int &$products_new = 0, - string $language = 'en', + language $language = language::en, array &$errors = [] ): void { try { @@ -112,7 +113,7 @@ final class catalog extends core try { if (!empty($row['identifier']) && !empty($row['name'])) { - // All required cells are filled in + // Required cells are filled in // Incrementing the counter of loaded categories ++$categories_loaded; @@ -123,36 +124,40 @@ final class catalog extends core // Declaring the variable with the category $category = null; - try { - // Initializing the category - $category = new category(document: category::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $row['identifier']], errors: $errors)[0] ?? null); + // Initializing the category + $category = category::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $row['identifier']], errors: $errors); + + if ($category instanceof category) { + // Initialized the category // Initializing name of the category - $category->name[$language] === $row['name'] || $category->name = [[$language => $row['name']]] + $category->name ?? []; + if (empty($category->name) || empty($category->name[$language->name]) || $category->name[$language->name] !== $row['name']) + $category->name = [$language->name => $row['name']] + ($category->name ?? []); // Initializing position of the category - $category->position === $row['position'] || $category->position = $row['position']; - } catch (exception $e) { - // Not found the category + if (empty($category->position) || $category->position === $row['position']) + $category->position = $row['position']; + } else { + // Not initialized the category // Creating the category - $_id = category::write((int) $row['identifier'], [$language => $row['name']], $row['position'] ?? null, $errors); + $_id = $created = category::write((int) $row['identifier'], [$language->name => $row['name']], $row['position'] ?? null, $errors); // Initializing the category - $category = new category(document: $created = category::_read('d._id == @_id', parameters: ['_id' => $_id], errors: $errors)[0] ?? null); + $category = category::_read('d._id == @_id', parameters: ['_id' => $_id], errors: $errors); // Incrementing the counter of created categories if ($created) ++$categories_created; }; if ($category instanceof category) { - // Found the category + // Initialized the category if (!empty($row['category'])) { // Received the ascendant category // Initializing the ascendant category - $ascendant = new category(document: category::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $row['category']], errors: $errors)[0] ?? null); + $ascendant = category::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $row['category']], errors: $errors); if ($ascendant instanceof category) { // Found the ascendant category @@ -265,7 +270,7 @@ final class catalog extends core try { if (!empty($row['identifier']) && !empty($row['name'])) { - // All required cells are filled in + // Required cells are filled in // Incrementing the counter of loaded products ++$products_loaded; @@ -276,45 +281,63 @@ final class catalog extends core // Declaring the variable with the product $product = null; - try { - // Initializing the product - $product = new product(document: product::_read('d.identifier == %u', parameters: ['identifier' => (int) $row['identifier']], errors: $errors)[0] ?? null); + // Initializing the product + $product = product::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $row['identifier']], errors: $errors); - // Initializing name of the category - $product->name[$language] === $row['name'] || $product->name = [[$language => $row['name']]] + $product->name ?? []; + if ($product instanceof product) { + // Initialized the product + + // Initializing name of the product + if (empty($product->name) || empty($product->name[$language->name]) || $product->name[$language->name] !== $row['name']) + $product->name = [$language->name => $row['name']] + ($product->name ?? []); + + // Initializing description of the product + if (empty($product->description) || empty($product->description[$language->name]) || $product->description[$language->name] !== $row['description']) + $product->description = [$language->name => $row['description']] + ($product->description ?? []); + + // Initializing brand of the product + if (empty($product->brand) || empty($product->brand[$language->name]) || $product->brand[$language->name] !== $row['brand']) + $product->brand = [$language->name => $row['brand']] + ($product->brand ?? []); + + // Initializing compatibility of the product + if (empty($product->compatibility) || empty($product->brand[$language->name]) || $product->compatibility[$language->name] !== $row['compatibility']) + $product->compatibility = [$language->name => $row['compatibility']] + ($product->compatibility ?? []); // Initializing position of the product - $product->position === $row['position'] || $product->position = $row['position']; - } catch (exception $e) { - // Not found the product + if (empty($product->position) || $product->position !== $row['position']) + $product->position = $row['position']; + } else { + // Not initialized the product // Creating the product $_id = product::write( (int) $row['identifier'], - [$language => $row['name']], - [$language => $row['description']], + [$language->name => $row['name']], + [$language->name => $row['description']], (float) $row['cost'], (float) $row['weight'], ['x' => $row['x'], 'y' => $row['y'], 'z' => $row['z']], + [$language->name => $row['brand']], + [$language->name => $row['compatibility']], $row['position'] ?? null, errors: $errors ); // Initializing the product - $product = new product(document: $created = product::_read(sprintf('d._id == "%s"', $_id), errors: $errors)[0] ?? null); + $product = $created = product::_read('d._id == @_id', parameters: ['_id' => $_id], errors: $errors); // Incrementing the counter of created products if ($created) ++$products_created; } if ($product instanceof product) { - // Found the product + // Initialized the product if (!empty($row['category'])) { // Received the category // Initializing the category - $category = new category(document: category::_read(sprintf('d.identifier == %u', (int) $row['category']), errors: $errors)[0] ?? null); + $category = category::_read(sprintf('d.identifier == %u', (int) $row['category']), errors: $errors); if ($category instanceof category) { // Found the ascendant category @@ -410,7 +433,7 @@ final class catalog extends core sort: 'd.updated DESC', amount: 100000, errors: $errors - ) as $document + ) ?? [] as $document ) { // Iterating over categories @@ -444,7 +467,7 @@ final class catalog extends core sort: 'd.updated DESC', amount: 100000, errors: $errors - ) as $document + ) ?? [] as $document ) { // Iterating over products diff --git a/mirzaev/arming_bot/system/models/core.php b/mirzaev/arming_bot/system/models/core.php index 6b82622..fd003d0 100755 --- a/mirzaev/arming_bot/system/models/core.php +++ b/mirzaev/arming_bot/system/models/core.php @@ -12,7 +12,7 @@ use mirzaev\arangodb\connection as arangodb, mirzaev\arangodb\collection, mirzaev\arangodb\enumerations\collection\type; -// Libraries for ArangoDB +// Library for ArangoDB use ArangoDBClient\Document as _document; // Built-in libraries @@ -40,6 +40,8 @@ class core extends model /** * Instance of the session of ArangoDB + * + * @todo ПЕРЕДЕЛАТЬ В php 8.4 */ protected static arangodb $arangodb; @@ -61,7 +63,7 @@ class core extends model * * @return void */ - public function __construct(bool $initialize = true, ?arangodb $arangodb = null) + public function __construct(bool $initialize = false, ?arangodb $arangodb = null) { // For the extends system parent::__construct($initialize); @@ -69,17 +71,8 @@ class core extends model if ($initialize) { // Initializing is requested - if (isset($arangodb)) { - // Recieved an instance of a session of ArangoDB - - // Writing an instance of a session of ArangoDB to the property - $this->__set('arangodb', $arangodb); - } else { - // Not recieved an instance of a session of ArangoDB - - // Initializing of an instance of a session of ArangoDB - $this->__get('arangodb'); - } + // Writing an instance of a session of ArangoDB to the property + self::$arangodb = $arangodb ?? new arangodb(require static::ARANGODB); } } @@ -104,7 +97,7 @@ class core extends model string $return = 'd', array $parameters = [], array &$errors = [] - ): _document|array|null { + ): _document|static|array|null { try { if (collection::initialize(static::COLLECTION, static::TYPE)) { // Initialized the collection @@ -117,22 +110,39 @@ class core extends model %s %s LIMIT @offset, @amount - RETURN @return + RETURN %s AQL, empty($filter) ? '' : "FILTER $filter", empty($sort) ? '' : "SORT $sort", + empty($return) ? 'd' : $return ), [ '@collection' => static::COLLECTION, 'offset' => --$page <= 0 ? 0 : $page * $amount, - 'amount' => $amount, - 'return' => $return + 'amount' => $amount ] + $parameters, - errors: $errors + errors: $errors ); + if ($result instanceof _document) { + // Received only 1 document and + + // Initializing the object + $object = new static; + + if (method_exists($object, '__document')) { + // Object can implement a document from ArangoDB + + // Writing the instance of document from ArangoDB to the implement object + $object->__document($result); + + // Exit (success) + return $object; + } + } + // Exit (success) - return is_array($result) ? $result : [$result]; + return $result; } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } catch (exception $e) { // Writing to registry of errors @@ -160,26 +170,9 @@ class core extends model { match ($name) { 'arangodb' => (function () use ($value) { - if ($this->__isset('arangodb')) { - // Is alredy initialized - - // Exit (fail) - throw new exception('Forbidden to reinitialize the session of ArangoDB ($this::$arangodb)', 500); - } else { - // Is not already initialized - - if ($value instanceof arangodb) { - // Recieved an appropriate value - - // Writing the property and exit (success) - self::$arangodb = $value; - } else { - // Recieved an inappropriate value - - // Exit (fail) - throw new exception('Session of ArangoDB ($this::$arangodb) is need to be mirzaev\arangodb\connection', 500); - } - } + if (isset(static::$arangodb)) throw new exception('Forbidden to reinitialize the session of ArangoDB ($this::$arangodb)', 500); + else if ($value instanceof arangodb) self::$arangodb = $value; + else throw new exception('Session of connection to ArangoDB ($this::$arangodb) is need to be mirzaev\arangodb\connection', 500); })(), default => parent::__set($name, $value) }; @@ -195,22 +188,6 @@ class core extends model public function __get(string $name): mixed { return match ($name) { - 'arangodb' => (function () { - try { - if (!$this->__isset('arangodb')) { - // Is not initialized - - // Initializing of a default value from settings - $this->__set('arangodb', new arangodb(require static::ARANGODB)); - } - - // Exit (success) - return self::$arangodb; - } catch (exception) { - // Exit (fail) - return null; - } - })(), default => parent::__get($name) }; } diff --git a/mirzaev/arming_bot/system/models/entry.php b/mirzaev/arming_bot/system/models/entry.php index 0080487..b43038a 100755 --- a/mirzaev/arming_bot/system/models/entry.php +++ b/mirzaev/arming_bot/system/models/entry.php @@ -227,7 +227,8 @@ final class entry extends core implements arangodb_document_interface if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) { // Initialized collections - if ($documents = collection::execute( + // Execute and exit (success) + return is_array($result = collection::execute( sprintf( <<<'AQL' FOR v IN 1..1 INBOUND @document GRAPH @graph @@ -241,18 +242,13 @@ final class entry extends core implements arangodb_document_interface empty($sort) ? '' : "SORT $sort", ), [ - 'grapth' => 'catalog', + 'graph' => 'catalog', 'document' => $document->getId(), 'offset' => --$page <= 0 ? $page = 0 : $page * $amount, 'amount' => $amount ], errors: $errors - )) { - // Fount entries - - // Возврат (успех) - return is_array($documents) ? $documents : [$documents]; - } else return []; + )) ? $result : [$result]; } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } else throw new exception('Failed to initialize ' . $document::TYPE . ' collection: ' . $document::COLLECTION); } catch (exception $e) { diff --git a/mirzaev/arming_bot/system/models/enumerations/language.php b/mirzaev/arming_bot/system/models/enumerations/language.php new file mode 100644 index 0000000..bae35ec --- /dev/null +++ b/mirzaev/arming_bot/system/models/enumerations/language.php @@ -0,0 +1,45 @@ + + */ +enum language +{ + case en; + case ru; + + /** + * Translate label of language + * + * @param language|null $language Language into which to translate + * + * @return string Translated label of language + * + * @todo + * 1. More languages + * 2. Cases??? + */ + public function translate(?language $language = language::en): string + { + // Exit (success) + return match ($this) { + language::en => match ($language) { + language::en => 'English', + language::ru => 'Английский' + }, + language::ru => match ($language) { + language::en => 'Russian', + language::ru => 'Русский' + } + }; + } +} diff --git a/mirzaev/arming_bot/system/models/enumerations/session.php b/mirzaev/arming_bot/system/models/enumerations/session.php index 29f648f..9ec0ab6 100644 --- a/mirzaev/arming_bot/system/models/enumerations/session.php +++ b/mirzaev/arming_bot/system/models/enumerations/session.php @@ -4,6 +4,14 @@ declare(strict_types=1); namespace mirzaev\arming_bot\models\enumerations; +/** + * Types of session verification + * + * @package mirzaev\arming_bot\models\enumerations + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author Arsen Mirzaev Tatyano-Muradovich + */ enum session { case hash_only; diff --git a/mirzaev/arming_bot/system/models/interfaces/document.php b/mirzaev/arming_bot/system/models/interfaces/document.php index 4539dc6..4015a49 100755 --- a/mirzaev/arming_bot/system/models/interfaces/document.php +++ b/mirzaev/arming_bot/system/models/interfaces/document.php @@ -7,9 +7,6 @@ namespace mirzaev\arming_bot\models\interfaces; // Library для ArangoDB use ArangoDBClient\Document as _document; -// Framework for ArangoDB -use mirzaev\arangodb\connection as arangodb; - /** * Interface for implementing a document instance from ArangoDB * diff --git a/mirzaev/arming_bot/system/models/product.php b/mirzaev/arming_bot/system/models/product.php index 5388d54..d3c943e 100755 --- a/mirzaev/arming_bot/system/models/product.php +++ b/mirzaev/arming_bot/system/models/product.php @@ -6,6 +6,7 @@ namespace mirzaev\arming_bot\models; // Files of the project use mirzaev\arming_bot\models\core, + mirzaev\arming_bot\models\enumerations\language, mirzaev\arming_bot\models\traits\document as arangodb_document_trait; // Framework for ArangoDB @@ -41,6 +42,8 @@ final class product extends core * @param float $cost Cost * @param float $weight Weight * @param array $dimensions Dimensions ['x' => 0.0, 'y' => 0.0, 'z' => 0.0] + * @param array|null $brand Brand [['en' => value], ['ru' => значение]] + * @param array|null $compatibility Compatibility [['en' => value], ['ru' => значение]] * @param array $images Images (first will be thumbnail) * @param int|null $position Position for sorting in the catalog (ASC) * @param array $data Data @@ -58,6 +61,8 @@ final class product extends core float $cost = 0, float $weight = 0, array $dimensions = ['x' => 0, 'y' => 0, 'z' => 0], + ?array $brand = [['en' => 'ERROR']], + ?array $compatibility = [['en' => 'ERROR']], array $images = [], ?int $position = null, array $data = [], @@ -81,6 +86,8 @@ final class product extends core 'y' => $dimensions['y'] ?? 0, 'z' => $dimensions['z'] ?? 0, ], + 'brand' => $brand, + 'compatibility' => $compatibility, 'images' => $images, 'position' => $position, 'version' => ROBOT_VERSION @@ -111,7 +118,8 @@ final class product extends core * @param int $page Page * @param int $amount Amount per page * @param string|null $return Return (AQL) - * @param string $language Language code (en, ru...) + * @param language $language Language code (en, ru...) + * @param array $parameters Binded parameters for placeholders ['placeholder' => parameter] * @param array &$errors Registry of errors * * @return array Массив с найденными товарами (может быть пустым) @@ -123,24 +131,32 @@ final class product extends core int $page = 1, int $amount = 100, ?string $return = 'd', - string $language = 'en', + language $language = language::en, + array $parameters = [], array &$errors = [] ): array { try { if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) { // Initialized the collection - // Initializing the query - $aql = <<<'AQL' - FOR d IN @@collection - AQL; + // Initializing parameters for search + if ($search) $parameters += [ + 'search' => $search, + 'analyzer' => 'text_' . $language->name + ]; - if ($search) { - // Requested search - - // Writing to the query - $aql .= <<<'AQL' - SEARCH + // Reading products + $documents = collection::execute( + sprintf( + <<<'AQL' + FOR d IN @@collection %s + %s + %s + LIMIT @offset, @amount + RETURN %s + AQL, + empty($search) ? '' : <<<'AQL' + SEARCH LEVENSHTEIN_MATCH( d.name.@language, TOKENS(@search, @analyzer)[0], @@ -158,35 +174,18 @@ final class product extends core tokens(@search, @analyzer)[0], 1, false - ) - AQL; - - // Adding sorting - if ($sort) $sort = "BM25(d) DESC, $sort"; - else $sort = "BM25(d) DESC"; - } - - // Reading products - $documents = collection::execute( - sprintf( - $aql . <<<'AQL' - %s - %s - LIMIT @offset, @amount - RETURN $s + ) AQL, empty($filter) ? '' : "FILTER $filter", - empty($sort) ? '' : "SORT $sort", + empty($search) ? (empty($sort) ? '' : "SORT $sort") : (empty($sort) ? "SORT BM25(d) DESC" : "SORT BM25(d) DESC, $sort"), empty($return) ? 'd' : $return ), [ - '@collection' => $search ? static::COLLECTION . 's_search' : static::COLLECTION, - 'search' => $search, - 'language' => $language, - 'analyzer' => "text_$language", + '@collection' => empty($search) ? static::COLLECTION : static::COLLECTION . 's_search', + 'language' => $language->name, 'offset' => --$page <= 0 ? $page = 0 : $page * $amount, 'amount' => $amount, - ], + ] + $parameters, errors: $errors ); @@ -196,7 +195,7 @@ final class product extends core // Exit (success) return is_array($documents) ? $documents : [$documents]; } else return []; - } else throw new exception('Failed to initialize ' . static::COLLECTION . ' collection: ' . static::COLLECTION); + } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } catch (exception $e) { // Writing to the registry of errors $errors[] = [ @@ -227,9 +226,9 @@ final class product extends core if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) { // Initialized the collection - if ($values = collection::execute( + if ($result = collection::execute( <<<'AQL' - FOR d IN @@collecton + FOR d IN @@collection RETURN DISTINCT @parameter AQL, [ @@ -241,7 +240,7 @@ final class product extends core // Found parameters // Exit (success) - return $values; + return is_array($result) ? $result : [$result]; } else return []; } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } catch (exception $e) { diff --git a/mirzaev/arming_bot/system/models/session.php b/mirzaev/arming_bot/system/models/session.php index 2ef0934..d6b7196 100755 --- a/mirzaev/arming_bot/system/models/session.php +++ b/mirzaev/arming_bot/system/models/session.php @@ -141,7 +141,7 @@ final class session extends core implements arangodb_document_interface // Initialized collections // Search for connected account - $document = collection::execute( + $result = collection::execute( <<__document($document); + if (method_exists($account, '__document')) { + // Object can implement a document from ArangoDB - // Exit (success) - return $account; + // Writing the instance of account document from ArangoDB to the implement object + $account->__document($result); + + // Exit (success) + return $account; + } } else return null; } else throw new exception('Failed to initialize ' . account::TYPE . ' collection: ' . account::COLLECTION); } else throw new exception('Failed to initialize ' . connect::TYPE . ' collection: ' . connect::COLLECTION); diff --git a/mirzaev/arming_bot/system/models/settings.php b/mirzaev/arming_bot/system/models/settings.php index bdfd74e..2fccc67 100755 --- a/mirzaev/arming_bot/system/models/settings.php +++ b/mirzaev/arming_bot/system/models/settings.php @@ -7,7 +7,8 @@ namespace mirzaev\arming_bot\models; // Files of the project use mirzaev\arming_bot\models\core, mirzaev\arming_bot\models\traits\document as arangodb_document_trait, - mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface; + mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface, + mirzaev\arming_bot\models\enumerations\language; // Framework for ArangoDB use mirzaev\arangodb\collection, @@ -51,7 +52,7 @@ final class settings extends core implements arangodb_document_interface // Initialized the collection // Search for active settings - $document = collection::execute( + $result = collection::execute( <<<'AQL' FOR d IN @@collection FILTER d.status == 'active' @@ -65,17 +66,24 @@ final class settings extends core implements arangodb_document_interface errors: $errors ); - if ($document instanceof _document) { + if ($result instanceof _document) { // Found active settings - // Initializing the implement object of the instance of settings document from ArangoDB + // Initializing the object $settings = new static; - // Writing the instance of settings document from ArangoDB to the implement object - $settings->__document($document); + if (method_exists($settings, '__document')) { + // Object can implement a document from ArangoDB - // Exit (success) - return $settings; + // Abstractioning of parameters + $result->language = language::{$result->language} ?? 'en'; + + // Writing the instance of settings document from ArangoDB to the implement object + $settings->__document($result); + + // Exit (success) + return $settings; + } } else if ($create) { // Not found active settings and requested their creating diff --git a/mirzaev/arming_bot/system/models/suspension.php b/mirzaev/arming_bot/system/models/suspension.php index cb5d832..ab7f2f3 100755 --- a/mirzaev/arming_bot/system/models/suspension.php +++ b/mirzaev/arming_bot/system/models/suspension.php @@ -6,8 +6,7 @@ namespace mirzaev\arming_bot\models; // Files of the project use mirzaev\arming_bot\models\core, - mirzaev\arming_bot\controllers\core as controller, - mirzaev\arming_bot\models\settings, + mirzaev\arming_bot\models\enumerations\language, mirzaev\arming_bot\models\traits\document as arangodb_document_trait, mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface; @@ -53,7 +52,7 @@ final class suspension extends core implements arangodb_document_interface // Initialized the collection // Search for active suspension - $document = collection::execute( + $result = collection::execute( <<<'AQL' FOR d IN @@collection FILTER d.end > @time @@ -68,17 +67,21 @@ final class suspension extends core implements arangodb_document_interface errors: $errors ); - if ($document instanceof _document) { - // Found active suspension + if ($result instanceof _document) { + // Found active settings - // Initializing the implement object of the instance of suspension document from ArangoDB + // Initializing the object $suspension = new static; - // Writing the instance of suspension document from ArangoDB to the implement object - $suspension->__document($document); + if (method_exists($suspension, '__document')) { + // Object can implement a document from ArangoDB - // Exit (success) - return $suspension; + // Writing the instance of suspension document from ArangoDB to the implement object + $suspension->__document($result); + + // Exit (success) + return $suspension; + } } else return null; } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } catch (exception $e) { @@ -98,17 +101,14 @@ final class suspension extends core implements arangodb_document_interface /** * Generate message about remaining time * - * @param string|null $language Language of the generated text (otherwise used from settings.language) + * @param language|null $language Language of the generated text (otherwise used from settings.language) * @param array &$errors Registry of errors * * @return string|null Text: "? days, ? hours and ? minutes" */ - public function message(?string $language = null, array &$errors = []): ?string + public function message(?language $language = language::en, array &$errors = []): ?string { try { - // Initializing default value - $language ??= controller::$settings?->language ?? 'en'; - // Initializing the time until the suspension ends $difference = date_diff(new datetime('@' . $this->document->end), new datetime()); @@ -118,54 +118,54 @@ final class suspension extends core implements arangodb_document_interface $difference->d, match ($difference->d > 20 ? $difference->d % 10 : $difference->d % 100) { 1 => match ($language) { - 'ru' => 'день', - 'en' => 'day', + language::en => 'day', + language::ru => 'день', default => 'day' }, 2, 3, 4 => match ($language) { - 'ru' => 'дня', - 'en' => 'days', + language::en => 'days', + language::ru => 'дня', default => 'days' }, default => match ($language) { - 'ru' => 'дней', - 'en' => 'days', + language::en => 'days', + language::ru => 'дней', default => 'days' } }, $difference->h, match ($difference->h > 20 ? $difference->h % 10 : $difference->h % 100) { 1 => match ($language) { - 'ru' => 'час', - 'en' => 'hours', + language::en => 'hours', + language::ru => 'час', default => 'hour' }, 2, 3, 4 => match ($language) { - 'ru' => 'часа', - 'en' => 'hours', + language::en => 'hours', + language::ru => 'часа', default => 'hours' }, default => match ($language) { - 'ru' => 'часов', - 'en' => 'hours', + language::en => 'hours', + language::ru => 'часов', default => 'hours' } }, $difference->i, match ($difference->i > 20 ? $difference->i % 10 : $difference->i % 100) { 1 => match ($language) { - 'ru' => 'минута', - 'en' => 'minute', + language::en => 'minute', + language::ru => 'минута', default => 'minute' }, 2, 3, 4 => match ($language) { - 'ru' => 'минуты', - 'en' => 'minutes', + language::en => 'minutes', + language::ru => 'минуты', default => 'minutes' }, default => match ($language) { - 'ru' => 'минут', - 'en' => 'minutes', + language::en => 'minutes', + language::ru => 'минут', default => 'minutes' } } diff --git a/mirzaev/arming_bot/system/models/telegram.php b/mirzaev/arming_bot/system/models/telegram.php index 6340fd3..bfa61bd 100755 --- a/mirzaev/arming_bot/system/models/telegram.php +++ b/mirzaev/arming_bot/system/models/telegram.php @@ -410,7 +410,7 @@ final class telegram extends core $products_deleted, $products_old, $products_new, - language: 'ru' + language: $account->language ?? settings::active()?->language ?? 'en' ); // Отправка сообщения @@ -489,7 +489,7 @@ final class telegram extends core $telegram = $ctx->getEffectiveUser(); // Инициализация аккаунта - $account = account::initialization($telegram->getId(), $telegram); + $account = account::initialize($telegram->getId(), $telegram); if ($account) { // Инициализирован аккаунт @@ -561,11 +561,11 @@ final class telegram extends core } // Инициализация сообщения - $message = "⚠️ *Работа приостановлена*\n*Оставшееся время\:* " . $suspension->message($account->language ?? controller::$settings?->language); + $message = "⚠️ *Работа приостановлена*\n*Оставшееся время\:* " . $suspension->message($account->language ?? settings::active()?->language ?? 'en'); // Добавление описания причины приостановки, если найдена if (!empty($suspension->description)) - $message .= "\n\n" . $suspension->description[$account->language ?? controller::$settings?->language] ?? array_values($suspension->description)[0]; + $message .= "\n\n" . $suspension->description[$account->language ?? settings::active()?->language ?? 'en'] ?? array_values($suspension->description)[0]; // Отправка сообщения $ctx->sendMessage($message) diff --git a/mirzaev/arming_bot/system/models/traits/document.php b/mirzaev/arming_bot/system/models/traits/document.php index e4e457d..26fe1c7 100755 --- a/mirzaev/arming_bot/system/models/traits/document.php +++ b/mirzaev/arming_bot/system/models/traits/document.php @@ -49,7 +49,7 @@ trait document parent::__construct($initialize, $arangodb); // Writing to the property - if ($document instanceof _document) $this->document = $document; + if ($document instanceof _document) $this->__document($document); else if ($document === null) throw new exception('Failed to initialize an instance of the document from ArangoDB'); } @@ -98,7 +98,6 @@ trait document { // Read a property from an instance of the ArangoDB document and exit (success) return match ($name) { - 'arangodb' => core::$arangodb, default => $this->document->{$name} }; } diff --git a/mirzaev/arming_bot/system/public/index.php b/mirzaev/arming_bot/system/public/index.php index 021664a..d4f7db0 100755 --- a/mirzaev/arming_bot/system/public/index.php +++ b/mirzaev/arming_bot/system/public/index.php @@ -41,8 +41,9 @@ $router ->write('/', 'catalog', 'index', 'GET') ->write('/search', 'catalog', 'search', 'POST') ->write('/session/connect/telegram', 'session', 'telegram', 'POST') - ->write('/product/$id', 'catalog', 'product', 'POST') - ->write('/$categories...', 'catalog', 'index', 'POST'); + ->write('/category/$identifier', 'catalog', 'index', 'POST') + ->write('/category', 'catalog', 'index', 'POST') + ->write('/product/$identifier', 'catalog', 'product', 'POST'); /* diff --git a/mirzaev/arming_bot/system/public/js/catalog.js b/mirzaev/arming_bot/system/public/js/catalog.js index 5bf6ec1..d9900f3 100755 --- a/mirzaev/arming_bot/system/public/js/catalog.js +++ b/mirzaev/arming_bot/system/public/js/catalog.js @@ -34,16 +34,11 @@ import("/js/core.js").then(() => // Write to the core core.catalog = class catalog { - /** - * Current position in hierarchy of the categories - */ - static categories = []; - /** * Registry of filters (instead of cookies) */ static filters = new Map([ - ['brand', null] + ["brand", null], ]); /** @@ -56,10 +51,12 @@ import("/js/core.js").then(() => * @return {void} */ static category(button, clean = true, force = false) { - // Initialize of the new category name - const category = button.getAttribute("data-category-name"); + // Initializing identifier of the category + const identifier = button.getAttribute( + "data-category-identifier", + ); - this._category(category, clean, force); + this._category(identifier, clean, force); } /** @@ -80,22 +77,22 @@ import("/js/core.js").then(() => /** * Select a category (system) * - * @param {HTMLElement} button Button of category + * @param {string} identifier Identifier of the category * @param {bool} clean Clear search bar? * * @return {Promise} Request to the server */ - static __category(category = "", clean = true) { + static __category(category, clean = true) { if (typeof category === "string") { - // + // Received required parameters - let urn; - if (category === "/" || category === "") urn = "/"; - else {urn = this.categories.length > 0 - ? `/${this.categories.join("/")}/${category}` - : `/${category}`;} - - return core.request(urn) + return core.request( + "/category" + + ("/" + category).replace(/^\/*/, "/").trim().replace( + /\/*$/, + "", + ), + ) .then((json) => { if ( json.errors !== null && @@ -114,11 +111,6 @@ import("/js/core.js").then(() => if (search instanceof HTMLElement) search.value = ""; } - // Write the category to position in the categories hierarchy - if (category !== "/" && category !== "") { - this.categories.push(category); - } - if ( typeof json.title === "string" && json.title.length > 0 @@ -288,9 +280,6 @@ import("/js/core.js").then(() => * @todo add animations of errors */ static __search(element) { - // Deinitialization of position in the categories hierarchy - this.categories = []; - return this.__category("/", false) .then(function () { core.request("/search", `text=${element.value}`) @@ -392,19 +381,19 @@ import("/js/core.js").then(() => /** * Open product card (interface) * - * @param {string} id Identifier of a product + * @param {string} identifier Identifier of a product * @param {bool} force Ignore the damper? * * @return {void} */ - static product(id, force = false) { - this._product(id, force); + static product(identifier, force = false) { + this._product(identifier, force); } /** * Open product card (damper) * - * @param {string} id Identifier of a product + * @param {string} identifier Identifier of a product * @param {bool} force Ignore the damper? * * @return {void} @@ -418,15 +407,15 @@ import("/js/core.js").then(() => /** * Open product card (system) * - * @param {string} id Identifier of a product + * @param {string} identifier Identifier of a product * * @return {Promise} Request to the server */ - static __product(id) { - if (typeof id === "number") { + static __product(identifier) { + if (typeof identifier === "number") { // - return core.request(`/product/${id}`) + return core.request(`/product/${identifier}`) .then((json) => { if ( json.errors !== null && @@ -455,15 +444,18 @@ import("/js/core.js").then(() => card.classList.add("card", "unselectable"); const h3 = document.createElement("h3"); - h3.setAttribute("title", json.product.id); - const title = document.createElement("span"); - title.classList.add("title"); - title.innerText = json.product.title; + const name = document.createElement("span"); + name.classList.add("name"); + name.setAttribute("title", json.product.identifier); + name.innerText = json.product.name; - const brand = document.createElement("small"); - brand.classList.add("brand"); - brand.innerText = json.product.brand; + const exit = document.createElement("a"); + exit.classList.add("exit"); + exit.setAttribute("type", "button"); + + const exit_icon = document.createElement("i"); + exit_icon.classList.add("icon", "close"); const images = document.createElement("div"); images.classList.add("images", "unselectable"); @@ -528,6 +520,13 @@ import("/js/core.js").then(() => images.append(image); } + const header = document.createElement("p"); + header.classList.add('header'); + + const brand = document.createElement("small"); + brand.classList.add("brand"); + brand.innerText = json.product.brand; + const description = document.createElement("p"); description.classList.add("description"); description.innerText = json.product.description; @@ -542,10 +541,24 @@ import("/js/core.js").then(() => const dimensions = document.createElement("small"); dimensions.classList.add("dimensions"); - dimensions.innerText = json.product.dimensions.x + - "x" + - json.product.dimensions.y + "x" + - json.product.dimensions.z; + + const x = json.product.dimensions.x; + const y = json.product.dimensions.y; + const z = json.product.dimensions.z; + + let formatted = ""; + + if (x !== '' ) formatted = x; + if (y !== '') { + if (formatted.length === 0) formatted = y; + else formatted += "x" + y; + } + if (z !== '') { + if (formatted.length === 0) formatted = z; + else formatted += "x" + z; + } + + dimensions.innerText = formatted; const weight = document.createElement("small"); weight.classList.add("weight"); @@ -555,10 +568,13 @@ import("/js/core.js").then(() => cost.classList.add("cost"); cost.innerText = json.product.cost + "р"; - h3.append(title); - h3.append(brand); + h3.append(name); + exit.append(exit_icon); + h3.append(exit); card.append(h3); card.append(images); + header.append(brand); + card.append(header); card.append(description); card.append(compatibility); footer.append(dimensions); @@ -581,8 +597,8 @@ import("/js/core.js").then(() => ); history.pushState( - { product_card: json.product.id }, - json.product.title, + { product_card: json.product.identifier }, + json.product.name, ); // блокировка закрытия карточки @@ -595,6 +611,8 @@ import("/js/core.js").then(() => wrap.remove(); wrap.removeEventListener("mousedown", _from); wrap.removeEventListener("touchstart", _from); + exit.removeEventListener("click", remove); + exit.removeEventListener("touch", remove); document.removeEventListener("click", close); document.removeEventListener("touch", close); window.removeEventListener("popstate", remove); @@ -615,13 +633,15 @@ import("/js/core.js").then(() => from = undefined; }; + exit.addEventListener("click", remove); + exit.addEventListener("touch", remove); document.addEventListener("click", close); document.addEventListener("touch", close); window.addEventListener("popstate", remove); if (width > card.offsetWidth) { images.hotline = new core.hotline( - json.product.id, + json.product.identfier, images, ); images.hotline.step = -0.3; diff --git a/mirzaev/arming_bot/system/public/themes/default/css/catalog/2columns.css b/mirzaev/arming_bot/system/public/themes/default/css/catalog/2columns.css index b837e1c..4cd747d 100644 --- a/mirzaev/arming_bot/system/public/themes/default/css/catalog/2columns.css +++ b/mirzaev/arming_bot/system/public/themes/default/css/catalog/2columns.css @@ -8,9 +8,10 @@ main>section[data-section="catalog"] { } main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"] { + --padding: 0.7rem; position: relative; height: 23px; - padding: unset; + padding: var(--padding); display: flex; justify-content: center; align-items: center; @@ -27,7 +28,9 @@ main>section[data-section="catalog"][data-catalog-type="categories"]:last-child } main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]:has(>img) { + min-width: calc(50% - var(--padding) * 2); height: 180px; + padding: unset; } main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]>img { @@ -37,7 +40,8 @@ main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[ width: 110%; height: 110%; object-fit: cover; - filter: blur(1px); + /* filter: blur(1px); */ + filter: brightness(60%); } main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]:hover>img { @@ -45,14 +49,12 @@ main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[ } main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]:has(>img)>p { - --padding: 0.7rem; position: absolute; left: var(--padding); bottom: var(--padding); right: var(--padding); margin: unset; width: min-content; - padding: var(--padding); border-radius: 0.75rem; background: var(--tg-theme-secondary-bg-color); } diff --git a/mirzaev/arming_bot/system/public/themes/default/css/icons/close.css b/mirzaev/arming_bot/system/public/themes/default/css/icons/close.css new file mode 100755 index 0000000..bc8f910 --- /dev/null +++ b/mirzaev/arming_bot/system/public/themes/default/css/icons/close.css @@ -0,0 +1,30 @@ +@charset "UTF-8"; + +i.icon.close { + --diameter: 22px; + box-sizing: border-box; + position: relative; + display: block; + width: var(--diameter); + height: var(--diameter); + border: 2px solid transparent; + border-radius: 40px; +} +i.icon.close::after, +i.icon.close::before { + content: ""; + display: block; + box-sizing: border-box; + position: absolute; + width: 16px; + height: 2px; + background: currentColor; + transform: rotate(45deg); + border-radius: 5px; + top: 8px; + left: 1px; +} +i.icon.close::after { + transform: rotate(-45deg); +} + diff --git a/mirzaev/arming_bot/system/public/themes/default/css/main.css b/mirzaev/arming_bot/system/public/themes/default/css/main.css index a755a67..19ae8a9 100755 --- a/mirzaev/arming_bot/system/public/themes/default/css/main.css +++ b/mirzaev/arming_bot/system/public/themes/default/css/main.css @@ -108,12 +108,15 @@ search:has(input:disabled) { backdrop-filter: contrast(0.5); } +*[type="button"] { + cursor: pointer; +} + :is(button, a[type="button"]) { padding: 8px 16px; display: flex; justify-content: center; align-items: center; - cursor: pointer; color: var(--tg-theme-button-text-color); background-color: var(--tg-theme-button-color); } diff --git a/mirzaev/arming_bot/system/public/themes/default/css/window.css b/mirzaev/arming_bot/system/public/themes/default/css/window.css index 696c46d..519086a 100755 --- a/mirzaev/arming_bot/system/public/themes/default/css/window.css +++ b/mirzaev/arming_bot/system/public/themes/default/css/window.css @@ -2,7 +2,7 @@ section#window { z-index: 1500; - position: absolute; + position: fixed; width: 100vw; height: 100vh; display: flex; @@ -24,7 +24,7 @@ section#window>div.card { section#window>div.card>h3 { margin: 0 0 0.5rem 0; height: 23px; - padding: 1rem 1.5rem; + padding: 1rem 0; display: inline-flex; align-items: center; gap: 1rem; @@ -32,16 +32,29 @@ section#window>div.card>h3 { background-color: var(--tg-theme-header-bg-color); } -section#window>div.card>h3>span.title { +section#window>div.card>h3>span.name { + margin-left: 1.5rem; overflow: hidden; text-overflow: ellipsis; } -section#window>div.card>h3>small.brand { +/* section#window>div.card>h3>small.brand { margin-left: auto; font-size: 0.8rem; font-weight: normal; color: var(--tg-theme-section-header-text-color); +} */ + +section#window>div.card>h3>a.exit[type="button"] { + margin-left: auto; + margin-right: 0.5rem; + padding: 0.6rem; + display: flex; + justify-content: center; + align-items: center; + font-weight: bold; + color: var(--tg-theme-section-header-text-color); + background-color: unset; } section#window>div.card>div.images { @@ -102,7 +115,7 @@ section#window>div.card>p { min-height: 1rem; padding: 0 1rem; overflow-y: scroll; - -webkit-overflow-scrolling: touch; + -webkit-overflow-scrolling: touch; } section#window>div.card>p:last-of-type { diff --git a/mirzaev/arming_bot/system/views/templater.php b/mirzaev/arming_bot/system/views/templater.php index d23f9a8..50e6a77 100755 --- a/mirzaev/arming_bot/system/views/templater.php +++ b/mirzaev/arming_bot/system/views/templater.php @@ -16,7 +16,8 @@ use mirzaev\minimal\controller; use Twig\Loader\FilesystemLoader, Twig\Environment as twig, Twig\Extra\Intl\IntlExtension as intl, - Twig\TwigFilter; + Twig\TwigFilter, + Twig\TwigFunction; // Built-in libraries @@ -43,13 +44,17 @@ final class templater extends controller implements ArrayAccess /** * Constructor of an instance * - * @param session|null $session An object implementing a session instance from ArangoDB - * @param account|null $account An object implementing an account instance from ArangoDB + * @param session|null $session The object implementing a session instance from ArangoDB + * @param account|null $account The object implementing an account instance from ArangoDB + * @param settings|null $settings The object implementing an account instance from ArangoDB * * @return void */ - public function __construct(?session $session = null, ?account $account = null) - { + public function __construct( + ?session $session = null, + ?account $account = null, + ?settings $settings = null + ) { // Initializing of an instance of twig $this->twig = new twig(new FilesystemLoader(VIEWS)); @@ -57,9 +62,41 @@ final class templater extends controller implements ArrayAccess $this->twig->addGlobal('theme', 'default'); $this->twig->addGlobal('server', $_SERVER); $this->twig->addGlobal('cookies', $_COOKIE); - $this->twig->addGlobal('settings', settings::active()); + $this->twig->addGlobal('settings', $settings); if (!empty($session?->status())) $this->twig->addGlobal('session', $session); if (!empty($account?->status())) $this->twig->addGlobal('account', $account); + $this->twig->addGlobal('language', $account?->language->name ?? $settings?->language->name ?? 'en'); + + + // Initialize function of dimensions formattinx + $this->twig->addFunction( + new TwigFunction( + 'format_dimensions', + function ( + string|int|float|null $x = null, + string|int|float|null $y = null, + string|int|float|null $z = null, + string|null $before = null, + string|null $after = null + ) { + // Initialzing the buffer of result + $result = ''; + + // Generating + if (!empty($x)) $result .= $x; + if (!empty($y)) + if (empty($result)) $result = "$y"; + else $result .= "x$y"; + if (!empty($z)) + if (empty($result)) $result = "$z"; + else $result .= "x$z"; + if (!empty($result)) $result = "$before$result$after"; + + // Exit (success) + return $result; + } + ) + ); // Initializing of twig extensions $this->twig->addExtension(new intl()); diff --git a/mirzaev/arming_bot/system/views/themes/default/catalog/elements/categories.html b/mirzaev/arming_bot/system/views/themes/default/catalog/elements/categories.html index 8a37554..d286602 100755 --- a/mirzaev/arming_bot/system/views/themes/default/catalog/elements/categories.html +++ b/mirzaev/arming_bot/system/views/themes/default/catalog/elements/categories.html @@ -2,11 +2,19 @@
{% for category in categories %} + {% if category.images %} - {{ category.name[ account.language ?? settings.language ?? 'en' ] }} -

{{ category.name[ account.language ?? settings.language ?? 'en' ] }}

+ {{ category.name[language] }} +

{{ category.name[language] }}

+ {% else %} + +

{{ category.name[language] }}

+
+ {% endif %} {% endfor %}
{% endif %} diff --git a/mirzaev/arming_bot/system/views/themes/default/catalog/elements/products/2columns.html b/mirzaev/arming_bot/system/views/themes/default/catalog/elements/products/2columns.html index f1b7b92..8934553 100755 --- a/mirzaev/arming_bot/system/views/themes/default/catalog/elements/products/2columns.html +++ b/mirzaev/arming_bot/system/views/themes/default/catalog/elements/products/2columns.html @@ -1,10 +1,9 @@ {% macro card(product) %} -{% set title = product.title.ru ~ ' ' ~ product.brand.ru ~ ' ' ~ product.dimensions.x ~ 'x' ~ product.dimensions.y ~ 'x' -~ product.dimensions.z ~ ' ' ~ product.weight ~ 'г' %} +{% set title = product.name[language] ~ ' ' ~ product.brand[language] ~ format_dimensions(product.dimensions.x, product.dimensions.y, product.dimensions.z, ' ') ~ ' ' ~ product.weight ~ 'г' %} {% endmacro %} - {% if products is not empty %}
diff --git a/mirzaev/arming_bot/system/views/themes/default/catalog/page.html b/mirzaev/arming_bot/system/views/themes/default/catalog/page.html index d56851a..1fb68e8 100755 --- a/mirzaev/arming_bot/system/views/themes/default/catalog/page.html +++ b/mirzaev/arming_bot/system/views/themes/default/catalog/page.html @@ -5,6 +5,7 @@ + {% endblock %} {% block main %}