Compare commits
2 Commits
5509b97148
...
ac7694f716
Author | SHA1 | Date | |
---|---|---|---|
Arsen Mirzaev Tatyano-Muradovich | ac7694f716 | ||
Arsen Mirzaev Tatyano-Muradovich | 16be453b07 |
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace mirzaev\arming_bot\controllers;
|
||||||
|
|
||||||
|
// Files of the project
|
||||||
|
use mirzaev\arming_bot\controllers\core,
|
||||||
|
mirzaev\arming_bot\models\session,
|
||||||
|
mirzaev\arming_bot\models\account as model;
|
||||||
|
|
||||||
|
// Framework for ArangoDB
|
||||||
|
use mirzaev\arangodb\document;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller of account
|
||||||
|
*
|
||||||
|
* @package mirzaev\arming_bot\controllers
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
final class account extends core
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Registry of errors
|
||||||
|
*/
|
||||||
|
protected array $errors = [
|
||||||
|
'session' => [],
|
||||||
|
'account' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write to the buffer
|
||||||
|
*
|
||||||
|
* @param array $parameters Parameters of the request (POST + GET)
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function write(array $parameters = []): void
|
||||||
|
{
|
||||||
|
if (!empty($parameters) && $this->account instanceof model) {
|
||||||
|
// Found data of the program and active account
|
||||||
|
|
||||||
|
foreach ($parameters as $name => $value) {
|
||||||
|
// Iterate over parameters
|
||||||
|
|
||||||
|
// Validation of the parameter
|
||||||
|
if (mb_strlen($value) > 4096) continue;
|
||||||
|
|
||||||
|
// Convert name to multidimensional array
|
||||||
|
foreach (array_reverse(explode('_', $name)) as $key) $parameter = [$key => $parameter ?? json_validate($value) ? json_decode($value, true, 10) : $value];
|
||||||
|
|
||||||
|
// Write data of to the buffer parameter in the implement object of account document from ArangoDB
|
||||||
|
$this->account->buffer = $parameter + $this->account->buffer ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write from implement object to account document from ArangoDB
|
||||||
|
document::update($this->account->__document(), $this->errors['account']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,14 +6,21 @@ namespace mirzaev\arming_bot\controllers;
|
||||||
|
|
||||||
// Files of the project
|
// Files of the project
|
||||||
use mirzaev\arming_bot\controllers\core,
|
use mirzaev\arming_bot\controllers\core,
|
||||||
|
mirzaev\arming_bot\models\enumerations\language,
|
||||||
mirzaev\arming_bot\models\entry,
|
mirzaev\arming_bot\models\entry,
|
||||||
mirzaev\arming_bot\models\category,
|
mirzaev\arming_bot\models\category,
|
||||||
mirzaev\arming_bot\models\product;
|
mirzaev\arming_bot\models\product,
|
||||||
|
mirzaev\arming_bot\models\menu;
|
||||||
|
|
||||||
|
// Library for ArangoDB
|
||||||
|
use ArangoDBClient\Document as _document;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller of catalog
|
* Controller of catalog
|
||||||
*
|
*
|
||||||
* @package mirzaev\arming_bot\controllers
|
* @package mirzaev\arming_bot\controllers
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
final class catalog extends core
|
final class catalog extends core
|
||||||
|
@ -24,6 +31,7 @@ final class catalog extends core
|
||||||
protected array $errors = [
|
protected array $errors = [
|
||||||
'session' => [],
|
'session' => [],
|
||||||
'account' => [],
|
'account' => [],
|
||||||
|
'menu' => [],
|
||||||
'catalog' => []
|
'catalog' => []
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -34,220 +42,372 @@ final class catalog extends core
|
||||||
*/
|
*/
|
||||||
public function index(array $parameters = []): ?string
|
public function index(array $parameters = []): ?string
|
||||||
{
|
{
|
||||||
// Initializing identifier of a category
|
// Validating
|
||||||
preg_match('/[\d]+/', $parameters['identifier'] ?? '', $matches);
|
if (!empty($parameters['category']) && preg_match('/[\d]+/', $parameters['category'], $matches)) $category = (int) $matches[0];
|
||||||
$identifier = $matches[0] ?? null;
|
|
||||||
|
|
||||||
// Initializint the buffer of respnse
|
if (isset($category)) {
|
||||||
$html = [];
|
// Received and validated identifier of the category
|
||||||
|
|
||||||
if (!empty($parameters['identifier'])) {
|
// Initialize of category
|
||||||
// Передана категория (идентификатор)
|
$category = category::_read('d.identifier == @identifier', parameters: ['identifier' => $category], errors: $this->errors['catalog']);
|
||||||
|
|
||||||
// Инициализация актуальной категории
|
|
||||||
$category = category::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $identifier], errors: $this->errors['catalog']);
|
|
||||||
|
|
||||||
if ($category instanceof category) {
|
if ($category instanceof category) {
|
||||||
// Found the category
|
// Found the category
|
||||||
|
|
||||||
// Поиск категорий или товаров входящих в актуальную категорию
|
// Write to the response buffer
|
||||||
|
$response['category'] = ['name' => $category->name ?? null];
|
||||||
|
|
||||||
|
// Search for categories that are descendants of $category
|
||||||
$entries = entry::search(
|
$entries = entry::search(
|
||||||
document: $category,
|
document: $category,
|
||||||
amount: 30,
|
amount: 50,
|
||||||
errors: $this->errors['catalog']
|
errors: $this->errors['catalog']
|
||||||
);
|
);
|
||||||
|
/* var_dump($entries); die; */
|
||||||
|
|
||||||
// Объявление буферов категорий и товаров (важно - в единственном числе, по параметру из базы данных)
|
// Initialize buffers of entries (in singular, by parameter from ArangoDB)
|
||||||
$category = $product = [];
|
$category = $product = [];
|
||||||
|
|
||||||
foreach ($entries as $entry) {
|
foreach ($entries as $entry) {
|
||||||
// Перебор вхождений
|
// Iterate over entries (descendands)
|
||||||
|
|
||||||
// Запись массивов категорий и товаров ($category и $product) в буфер глобальной переменной шаблонизатора
|
// Write entry to the buffer of entries (sort by $category and $product)
|
||||||
${$entry->_type}[] = $entry;
|
${$entry->_type}[] = $entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Запись категорий из буфера в глобальную переменную шаблонизатора
|
// Write to the buffer of global variables of view templater
|
||||||
$this->view->categories = $category;
|
$this->view->categories = $category ?? null;
|
||||||
|
|
||||||
// Запись товаров из буфера в глобальную переменную шаблонизатора
|
// Write to the buffer of global variables of view templater
|
||||||
$this->view->products = $product;
|
$this->view->products = $product ?? null;
|
||||||
|
|
||||||
// Generating filters
|
/* var_dump($this->view->products); die; */
|
||||||
$this->view->filters = [
|
|
||||||
'brands' => product::collect(
|
|
||||||
'd.brand.' . $this->language->name,
|
|
||||||
errors: $this->errors['catalog']
|
|
||||||
)
|
|
||||||
];
|
|
||||||
|
|
||||||
// Generating HTML and writing to the buffer of response
|
// Delete buffers
|
||||||
$html = [
|
unset($category, $product);
|
||||||
'filters' => count($this->view->products) > 0 ? $this->view->render('catalog/elements/filters.html') : null,
|
|
||||||
'products' => $this->view->render('catalog/elements/products/2columns.html')
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!isset($parameters['category'])) {
|
||||||
// Не передана категория
|
// Not received identifie of the category
|
||||||
|
|
||||||
// Поиск категорий: "categories" (самый верхний уровень)
|
// Search for root ascendants categories
|
||||||
$this->view->categories = entry::ascendants(descendant: new category, errors: $this->errors['catalog']);
|
$this->view->categories = entry::ascendants(descendant: new category, errors: $this->errors['catalog']) ?? null;
|
||||||
|
}
|
||||||
|
// Validating
|
||||||
|
if (!empty($parameters['product']) && preg_match('/[\d]+/', $parameters['product'], $matches)) $product = (int) $matches[0];
|
||||||
|
|
||||||
|
if (isset($product)) {
|
||||||
|
// Received and validated identifier of the product
|
||||||
|
|
||||||
|
// Search for the product data and write to the buffer of global variables of view templater
|
||||||
|
$this->view->product = product::read(
|
||||||
|
filter: "d.identifier == @identifier && d.deleted != true && d.hidden != true",
|
||||||
|
sort: 'd.created DESC',
|
||||||
|
amount: 1,
|
||||||
|
return: '{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' => $product],
|
||||||
|
language: $this->language,
|
||||||
|
errors: $this->errors['catalog']
|
||||||
|
)[0]?->getAll() ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intializing buffer of query parameters
|
||||||
|
$_parameters = [];
|
||||||
|
|
||||||
|
// Initializing buffer of filters query (AQL)
|
||||||
|
$_filters = 'd.deleted != true && d.hidden != true';
|
||||||
|
|
||||||
|
// Search among products in the $category
|
||||||
|
if (isset($this->view->products) && count($this->view->products) > 0) {
|
||||||
|
// Amount of rendered products is more than 0
|
||||||
|
|
||||||
|
// Write to the buffer of filters query (AQL)
|
||||||
|
$_filters .= ' && POSITION(["' . implode('", "', array_map(fn(_document $document): string => $document->getId(), $this->view->products)) . '"], d._id)';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validating
|
||||||
|
if (!empty($parameters['brand']) && preg_match('/[\w\s]+/u', urldecode($parameters['brand']), $matches)) $brand = $matches[0];
|
||||||
|
|
||||||
|
if (isset($brand)) {
|
||||||
|
// Received and validated filter by brand
|
||||||
|
|
||||||
|
// Writing to the account buffer
|
||||||
|
$this->account?->write(
|
||||||
|
[
|
||||||
|
'catalog' => [
|
||||||
|
'filters' => [
|
||||||
|
'brand' => $brand
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Writing to the session buffer
|
||||||
|
$this->session?->write(
|
||||||
|
[
|
||||||
|
'catalog' => [
|
||||||
|
'filters' => [
|
||||||
|
'brand' => $brand
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Writing to the buffer of filters query (AQL)
|
||||||
|
$_filters .= ' && d.brand.@language == @brand';
|
||||||
|
|
||||||
|
// Writing to the buffer of query parameters
|
||||||
|
$_parameters['brand'] = $brand;
|
||||||
|
} else {
|
||||||
|
// Not received or not validated filter by brand
|
||||||
|
|
||||||
|
// Writing to the account buffer
|
||||||
|
$this->account?->write(
|
||||||
|
[
|
||||||
|
'catalog' => [
|
||||||
|
'filters' => [
|
||||||
|
'brand' => null
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Writing to the session buffer
|
||||||
|
$this->session?->write(
|
||||||
|
[
|
||||||
|
'catalog' => [
|
||||||
|
'filters' => [
|
||||||
|
'brand' => null
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize buffer of filters query (AQL)
|
||||||
|
$_sort = 'd.position ASC, d.name ASC, d.created DESC';
|
||||||
|
|
||||||
|
// Validating
|
||||||
|
if (!empty($parameters['sort']) && preg_match('/[\w\s]+/u', $parameters['sort'], $matches)) $sort = $matches[0];
|
||||||
|
|
||||||
|
if (isset($sort)) {
|
||||||
|
// Received and validated sort
|
||||||
|
|
||||||
|
// Writing to the account buffer
|
||||||
|
$this->account?->write(
|
||||||
|
[
|
||||||
|
'catalog' => [
|
||||||
|
'sort' => $sort
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Writing to the session buffer
|
||||||
|
$this->session?->write(
|
||||||
|
[
|
||||||
|
'catalog' => [
|
||||||
|
'sort' => $sort
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Write to the buffer of sort query (AQL)
|
||||||
|
$_sort = "d.@sort DESC, $_sort";
|
||||||
|
|
||||||
|
// Write to the buffer of query parameters
|
||||||
|
$_parameters['sort'] = $sort;
|
||||||
|
} else {
|
||||||
|
// Not received or not validated filter by brand
|
||||||
|
|
||||||
|
// Writing to the account buffer
|
||||||
|
$this->account?->write(
|
||||||
|
[
|
||||||
|
'catalog' => [
|
||||||
|
'sort' => null
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Writing to the session buffer
|
||||||
|
$this->session?->write(
|
||||||
|
[
|
||||||
|
'catalog' => [
|
||||||
|
'sort' => null
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validating @todo add throwing errors
|
||||||
|
if (!empty($parameters['text']) && preg_match('/[\w\s]+/u', urldecode($parameters['text']), $matches) && mb_strlen($matches[0]) > 2) $text = $matches[0];
|
||||||
|
|
||||||
|
if (isset($text)) {
|
||||||
|
// Received and validated text
|
||||||
|
|
||||||
|
// Writing to the account buffer
|
||||||
|
$this->account?->write(
|
||||||
|
[
|
||||||
|
'catalog' => [
|
||||||
|
'search' => [
|
||||||
|
'text' => $text
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Writing to the session buffer
|
||||||
|
$this->session?->write(
|
||||||
|
[
|
||||||
|
'catalog' => [
|
||||||
|
'search' => [
|
||||||
|
'text' => $text
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Search for products and write to the buffer of global variables of view templater
|
||||||
|
$this->view->products = product::read(
|
||||||
|
search: $text ?? null,
|
||||||
|
filter: $_filters,
|
||||||
|
sort: $_sort,
|
||||||
|
amount: 50, // @todo pagination
|
||||||
|
language: $this->language,
|
||||||
|
parameters: $_parameters,
|
||||||
|
return: 'DISTINCT MERGE(d, {name: d.name.@language, description: d.description.@language, compatibility: d.compatibility.@language})',
|
||||||
|
errors: $this->errors['catalog']
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Not received or not validated filter by brand
|
||||||
|
|
||||||
|
// Writing to the account buffer
|
||||||
|
$this->account?->write(
|
||||||
|
[
|
||||||
|
'catalog' => [
|
||||||
|
'search' => [
|
||||||
|
'text' => null
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Writing to the session buffer
|
||||||
|
$this->session?->write(
|
||||||
|
[
|
||||||
|
'catalog' => [
|
||||||
|
'search' => [
|
||||||
|
'text' => null
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->view->products) && count($this->view->products) > 0) {
|
||||||
|
// Amount of rendered products is more than 0
|
||||||
|
|
||||||
|
// Search for filters and write to the buffer of global variables of view templater
|
||||||
|
$this->view->filters = [
|
||||||
|
'brands' => product::collect(
|
||||||
|
return: 'd.brand.@language',
|
||||||
|
products: array_map(fn(_document $document): string => $document->getId(), $this->view->products),
|
||||||
|
language: $this->language,
|
||||||
|
errors: $this->errors['catalog']
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($menu)) {
|
||||||
|
//
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Not received ... menu
|
||||||
|
|
||||||
|
// Search for filters and write to the buffer of global variables of view templater
|
||||||
|
$this->view->menu = menu::_read(
|
||||||
|
return: 'MERGE(d, { name: d.name.@language })',
|
||||||
|
sort: 'd.position ASC, d.created DESC, d._key DESC',
|
||||||
|
amount: 4,
|
||||||
|
parameters: ['language' => $this->language->name],
|
||||||
|
errors: $this->errors['menu']
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
// GET request
|
// GET request
|
||||||
|
|
||||||
|
if (!empty($this->view->product)) {
|
||||||
|
// Initialized the product data
|
||||||
|
|
||||||
|
// Writing javascript code that has been executed after core and damper has been loaded
|
||||||
|
$this->view->javascript = [<<<javascript
|
||||||
|
if (typeof _window === 'undefined') {
|
||||||
|
_window = setTimeout(() => core.catalog.product_system('$product'), 500);
|
||||||
|
}
|
||||||
|
javascript] + ($this->view->javascript ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
// Exit (success)
|
// Exit (success)
|
||||||
return $this->view->render('catalog/page.html');
|
return $this->view->render('catalog/page.html');
|
||||||
} else if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
} else if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
// POST request
|
// POST request
|
||||||
|
|
||||||
// Initializing a response headers
|
// Initializing the buffer of response
|
||||||
header('Content-Type: application/json');
|
$response = [
|
||||||
header('Content-Encoding: none');
|
'title' => $this->language === language::ru ? 'Каталог' : 'Catalog' // @see https://git.mirzaev.sexy/mirzaev/huesos/issues/1
|
||||||
header('X-Accel-Buffering: no');
|
];
|
||||||
|
|
||||||
// Initializing of the output buffer
|
if (isset($this->view->categories)) {
|
||||||
ob_start();
|
// Initialized categories
|
||||||
|
|
||||||
// Generating the reponse
|
// Render HTML-code of categories and write to the response buffer
|
||||||
echo json_encode(
|
$response['categories'] = $this->view->render('catalog/elements/categories.html');
|
||||||
[
|
|
||||||
'title' => $title ?? '',
|
|
||||||
'html' => $html + [
|
|
||||||
'categories' => $this->view->render('catalog/elements/categories.html'),
|
|
||||||
],
|
|
||||||
'errors' => $this->errors
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Initializing a response headers
|
|
||||||
header('Content-Length: ' . ob_get_length());
|
|
||||||
|
|
||||||
// Sending and deinitializing of the output buffer
|
|
||||||
ob_end_flush();
|
|
||||||
flush();
|
|
||||||
|
|
||||||
// Exit (success)
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit (fail)
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search
|
|
||||||
*
|
|
||||||
* @param array $parameters Parameters of the request (POST + GET)
|
|
||||||
*/
|
|
||||||
public function search(array $parameters = []): ?string
|
|
||||||
{
|
|
||||||
// Initializing of text fore search
|
|
||||||
preg_match('/[\w\s]+/u', $parameters['text'] ?? '', $matches);
|
|
||||||
$text = $matches[0] ?? null;
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
// POST request
|
|
||||||
|
|
||||||
// Search for products
|
|
||||||
$this->view->products = isset($text) ? product::read(
|
|
||||||
search: $text,
|
|
||||||
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']
|
|
||||||
) : [];
|
|
||||||
|
|
||||||
// Initializing a response headers
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
header('Content-Encoding: none');
|
|
||||||
header('X-Accel-Buffering: no');
|
|
||||||
|
|
||||||
// Initializing of the output buffer
|
|
||||||
ob_start();
|
|
||||||
|
|
||||||
// Generating the reponse
|
|
||||||
echo json_encode(
|
|
||||||
[
|
|
||||||
'title' => $title ?? '',
|
|
||||||
'html' => [
|
|
||||||
'products' => $this->view->render('catalog/elements/products/2columns.html')
|
|
||||||
],
|
|
||||||
'errors' => $this->errors
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Initializing a response headers
|
|
||||||
header('Content-Length: ' . ob_get_length());
|
|
||||||
|
|
||||||
// Sending and deinitializing of the output buffer
|
|
||||||
ob_end_flush();
|
|
||||||
flush();
|
|
||||||
|
|
||||||
// Exit (success)
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit (fail)
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Product
|
|
||||||
*
|
|
||||||
* @param array $parameters Parameters of the request (POST + GET)
|
|
||||||
*/
|
|
||||||
public function product(array $parameters = []): ?string
|
|
||||||
{
|
|
||||||
// Initializing identifier of a product
|
|
||||||
preg_match('/[\d]+/', $parameters['identifier'] ?? '', $matches);
|
|
||||||
$identifier = $matches[0] ?? null;
|
|
||||||
|
|
||||||
if (!empty($identifier)) {
|
|
||||||
// Received identifier of the product
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
// POST request
|
|
||||||
|
|
||||||
// Search for products
|
|
||||||
$product = product::read(
|
|
||||||
filter: "d.identifier == @identifier && d.deleted != true && d.hidden != true",
|
|
||||||
sort: 'd.created DESC',
|
|
||||||
amount: 1,
|
|
||||||
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();
|
|
||||||
|
|
||||||
// Initializing a response headers
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
header('Content-Encoding: none');
|
|
||||||
header('X-Accel-Buffering: no');
|
|
||||||
|
|
||||||
// Initializing of the output buffer
|
|
||||||
ob_start();
|
|
||||||
|
|
||||||
// Generating the reponse
|
|
||||||
echo json_encode(
|
|
||||||
[
|
|
||||||
'product' => $product,
|
|
||||||
'errors' => $this->errors
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Initializing a response headers
|
|
||||||
header('Content-Length: ' . ob_get_length());
|
|
||||||
|
|
||||||
// Sending and deinitializing of the output buffer
|
|
||||||
ob_end_flush();
|
|
||||||
flush();
|
|
||||||
|
|
||||||
// Exit (success)
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($this->view->product)) {
|
||||||
|
// Initialized product
|
||||||
|
|
||||||
|
// Writing data of the product to the response buffer @todo GENERATE THIS ON THE SERVER
|
||||||
|
$response['product'] = $this->view->product;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->view->products)) {
|
||||||
|
// Initialized products
|
||||||
|
|
||||||
|
// Render HTML-code of products and write to the response buffer
|
||||||
|
$response['products'] = $this->view->render('catalog/elements/products.html');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->view->filters)) {
|
||||||
|
// Initialized filters
|
||||||
|
|
||||||
|
// Render HTML-code of filters and write to the response buffer
|
||||||
|
$response['filters'] = $this->view->render('catalog/elements/filters.html');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initializing a response headers
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
header('Content-Encoding: none');
|
||||||
|
header('X-Accel-Buffering: no');
|
||||||
|
|
||||||
|
// Initializing of the output buffer
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
// Generating the reponse
|
||||||
|
echo json_encode(
|
||||||
|
$response + [
|
||||||
|
'errors' => $this->errors
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initializing a response headers
|
||||||
|
header('Content-Length: ' . ob_get_length());
|
||||||
|
|
||||||
|
// Sending and deinitializing of the output buffer
|
||||||
|
ob_end_flush();
|
||||||
|
flush();
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit (fail)
|
// Exit (fail)
|
||||||
|
|
|
@ -20,6 +20,8 @@ use mirzaev\minimal\controller;
|
||||||
* Core of controllers
|
* Core of controllers
|
||||||
*
|
*
|
||||||
* @package mirzaev\arming_bot\controllers
|
* @package mirzaev\arming_bot\controllers
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
class core extends controller
|
class core extends controller
|
||||||
|
|
|
@ -12,6 +12,8 @@ use mirzaev\arming_bot\controllers\core,
|
||||||
* Index controller
|
* Index controller
|
||||||
*
|
*
|
||||||
* @package mirzaev\arming_bot\controllers
|
* @package mirzaev\arming_bot\controllers
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
final class index extends core
|
final class index extends core
|
||||||
|
|
|
@ -16,6 +16,8 @@ use mirzaev\arangodb\document;
|
||||||
* Controller of session
|
* Controller of session
|
||||||
*
|
*
|
||||||
* @package mirzaev\arming_bot\controllers
|
* @package mirzaev\arming_bot\controllers
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
final class session extends core
|
final class session extends core
|
||||||
|
@ -153,14 +155,14 @@ final class session extends core
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect session to the telegram account
|
* Write to the buffer
|
||||||
*
|
*
|
||||||
* @param array $parameters Parameters of the request (POST + GET)
|
* @param array $parameters Parameters of the request (POST + GET)
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function write(array $parameters = []): ?string
|
public function write(array $parameters = []): void
|
||||||
{
|
{
|
||||||
if (!empty($parameters) && $this->session instanceof model) {
|
if (!empty($parameters) && $this->session instanceof model) {
|
||||||
// Found data of the program and active session
|
// Found data of the program and active session
|
||||||
|
@ -171,18 +173,15 @@ final class session extends core
|
||||||
// Validation of the parameter
|
// Validation of the parameter
|
||||||
if (mb_strlen($value) > 4096) continue;
|
if (mb_strlen($value) > 4096) continue;
|
||||||
|
|
||||||
// Write data of the program to the implement object of session document from ArangoDB
|
// Convert name to multidimensional array
|
||||||
$this->session->{$name} = json_decode($value, true, 10);
|
foreach (array_reverse(explode('_', $name)) as $key) $parameter = [$key => $parameter ?? json_validate($value) ? json_decode($value, true, 10) : $value];
|
||||||
|
|
||||||
|
// Write data of to the buffer parameter in the implement object of session document from ArangoDB
|
||||||
|
$this->session->buffer = $parameter + ($this->session->buffer ?? []);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write from implement object to session document from ArangoDB
|
// Write from implement object to session document from ArangoDB
|
||||||
document::update($this->session->__document(), $this->errors['session']);
|
document::update($this->session->__document(), $this->errors['session']);
|
||||||
|
|
||||||
// Exit (success)
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit (fail)
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace mirzaev\arming_bot\models;
|
||||||
use mirzaev\arming_bot\models\core,
|
use mirzaev\arming_bot\models\core,
|
||||||
mirzaev\arming_bot\models\traits\status,
|
mirzaev\arming_bot\models\traits\status,
|
||||||
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
||||||
|
mirzaev\arming_bot\models\traits\buffer,
|
||||||
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;
|
mirzaev\arming_bot\models\enumerations\language;
|
||||||
|
|
||||||
|
@ -34,7 +35,9 @@ use exception;
|
||||||
*/
|
*/
|
||||||
final class account extends core implements arangodb_document_interface
|
final class account extends core implements arangodb_document_interface
|
||||||
{
|
{
|
||||||
use status, arangodb_document_trait;
|
use status, arangodb_document_trait, buffer {
|
||||||
|
buffer::write as write;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the collection in ArangoDB
|
* Name of the collection in ArangoDB
|
||||||
|
|
|
@ -4,17 +4,19 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\arming_bot\models;
|
namespace mirzaev\arming_bot\models;
|
||||||
|
|
||||||
|
// Files of the project
|
||||||
|
use mirzaev\arming_bot\models\interfaces\collection as collection_interface;
|
||||||
|
|
||||||
// Framework for PHP
|
// Framework for PHP
|
||||||
use mirzaev\minimal\model;
|
use mirzaev\minimal\model;
|
||||||
|
|
||||||
// Framework for ArangoDB
|
|
||||||
use mirzaev\arangodb\connection as arangodb,
|
|
||||||
mirzaev\arangodb\collection,
|
|
||||||
mirzaev\arangodb\enumerations\collection\type;
|
|
||||||
|
|
||||||
// Library for ArangoDB
|
// Library for ArangoDB
|
||||||
use ArangoDBClient\Document as _document;
|
use ArangoDBClient\Document as _document;
|
||||||
|
|
||||||
|
// Framework for ArangoDB
|
||||||
|
use mirzaev\arangodb\connection as arangodb,
|
||||||
|
mirzaev\arangodb\collection;
|
||||||
|
|
||||||
// Built-in libraries
|
// Built-in libraries
|
||||||
use exception;
|
use exception;
|
||||||
|
|
||||||
|
@ -26,7 +28,7 @@ use exception;
|
||||||
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
class core extends model
|
class core extends model implements collection_interface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Postfix for name of models files
|
* Postfix for name of models files
|
||||||
|
@ -45,16 +47,6 @@ class core extends model
|
||||||
*/
|
*/
|
||||||
protected static arangodb $arangodb;
|
protected static arangodb $arangodb;
|
||||||
|
|
||||||
/**
|
|
||||||
* Name of the collection in ArangoDB
|
|
||||||
*/
|
|
||||||
public const string COLLECTION = 'THIS_COLLECTION_SHOULD_NOT_EXIST';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type of the collection in ArangoDB
|
|
||||||
*/
|
|
||||||
public const type TYPE = type::document;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor of an instance
|
* Constructor of an instance
|
||||||
*
|
*
|
||||||
|
@ -158,6 +150,7 @@ class core extends model
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write
|
* Write
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace mirzaev\arming_bot\models\interfaces;
|
||||||
|
|
||||||
|
// Framework for ArangoDB
|
||||||
|
use mirzaev\arangodb\enumerations\collection\type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for implementing a collection from ArangoDB
|
||||||
|
*
|
||||||
|
* @package mirzaev\arming_bot\models\traits
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
interface collection
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Name of the collection in ArangoDB
|
||||||
|
*/
|
||||||
|
public const string COLLECTION = 'THIS_COLLECTION_SHOULD_NOT_EXIST';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of the collection in ArangoDB
|
||||||
|
*/
|
||||||
|
public const type TYPE = type::document;
|
||||||
|
}
|
|
@ -13,6 +13,8 @@ use ArangoDBClient\Document as _document;
|
||||||
* @param _document $document An instance of the ArangoDB document from ArangoDB (protected readonly)
|
* @param _document $document An instance of the ArangoDB document from ArangoDB (protected readonly)
|
||||||
*
|
*
|
||||||
* @package mirzaev\arming_bot\models\traits
|
* @package mirzaev\arming_bot\models\traits
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
interface document
|
interface document
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model of menu
|
||||||
|
*
|
||||||
|
* @package mirzaev\arming_bot\models
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
final class menu extends core implements arangodb_document_interface
|
||||||
|
{
|
||||||
|
use arangodb_document_trait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the collection in ArangoDB
|
||||||
|
*/
|
||||||
|
final public const string COLLECTION = 'menu';
|
||||||
|
}
|
|
@ -122,7 +122,7 @@ final class product extends core
|
||||||
* @param array $parameters Binded parameters for placeholders ['placeholder' => parameter]
|
* @param array $parameters Binded parameters for placeholders ['placeholder' => parameter]
|
||||||
* @param array &$errors Registry of errors
|
* @param array &$errors Registry of errors
|
||||||
*
|
*
|
||||||
* @return array Массив с найденными товарами (может быть пустым)
|
* @return array Found products (can be empty)
|
||||||
*/
|
*/
|
||||||
public static function read(
|
public static function read(
|
||||||
?string $search = null,
|
?string $search = null,
|
||||||
|
@ -130,7 +130,7 @@ final class product extends core
|
||||||
?string $sort = 'd.position ASC, d.created DESC',
|
?string $sort = 'd.position ASC, d.created DESC',
|
||||||
int $page = 1,
|
int $page = 1,
|
||||||
int $amount = 100,
|
int $amount = 100,
|
||||||
?string $return = 'd',
|
?string $return = 'DISTINCT d',
|
||||||
language $language = language::en,
|
language $language = language::en,
|
||||||
array $parameters = [],
|
array $parameters = [],
|
||||||
array &$errors = []
|
array &$errors = []
|
||||||
|
@ -163,22 +163,22 @@ final class product extends core
|
||||||
1,
|
1,
|
||||||
false
|
false
|
||||||
) OR
|
) OR
|
||||||
levenshtein_match(
|
LEVENSHTEIN_MATCH(
|
||||||
d.description.@language,
|
d.description.@language,
|
||||||
tokens(@search, @analyzer)[0],
|
TOKENS(@search, @analyzer)[0],
|
||||||
1,
|
1,
|
||||||
false
|
false
|
||||||
) OR
|
) OR
|
||||||
levenshtein_match(
|
LEVENSHTEIN_MATCH(
|
||||||
d.compatibility.@language,
|
d.compatibility.@language,
|
||||||
tokens(@search, @analyzer)[0],
|
TOKENS(@search, @analyzer)[0],
|
||||||
1,
|
1,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
AQL,
|
AQL,
|
||||||
empty($filter) ? '' : "FILTER $filter",
|
empty($filter) ? '' : "FILTER $filter",
|
||||||
empty($search) ? (empty($sort) ? '' : "SORT $sort") : (empty($sort) ? "SORT BM25(d) DESC" : "SORT BM25(d) DESC, $sort"),
|
empty($search) ? (empty($sort) ? '' : "SORT $sort") : (empty($sort) ? "SORT BM25(d) DESC" : "SORT BM25(d) DESC, $sort"),
|
||||||
empty($return) ? 'd' : $return
|
empty($return) ? 'DISTINCT d' : $return
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
'@collection' => empty($search) ? static::COLLECTION : static::COLLECTION . 's_search',
|
'@collection' => empty($search) ? static::COLLECTION : static::COLLECTION . 's_search',
|
||||||
|
@ -214,12 +214,16 @@ final class product extends core
|
||||||
* Collect parameter from all products
|
* Collect parameter from all products
|
||||||
*
|
*
|
||||||
* @param string $return Return (AQL path)
|
* @param string $return Return (AQL path)
|
||||||
|
* @param array $products Array with products system identifiers ["_id", "_id", "_id"...]
|
||||||
* @param array &$errors Registry of errors
|
* @param array &$errors Registry of errors
|
||||||
*
|
*
|
||||||
* @return array Array with found unique parameter values from all products (can be empty)
|
* @return array Array with found unique parameter values from all products (can be empty)
|
||||||
*/
|
*/
|
||||||
public static function collect(
|
public static function collect(
|
||||||
string $return = 'd._key',
|
string $return = 'd._key',
|
||||||
|
array $products = [],
|
||||||
|
language $language = language::en,
|
||||||
|
array $parameters = [],
|
||||||
array &$errors = []
|
array &$errors = []
|
||||||
): array {
|
): array {
|
||||||
try {
|
try {
|
||||||
|
@ -230,13 +234,16 @@ final class product extends core
|
||||||
sprintf(
|
sprintf(
|
||||||
<<<'AQL'
|
<<<'AQL'
|
||||||
FOR d IN @@collection
|
FOR d IN @@collection
|
||||||
|
%s
|
||||||
RETURN DISTINCT %s
|
RETURN DISTINCT %s
|
||||||
AQL,
|
AQL,
|
||||||
|
empty($products) ? '' : 'FILTER POSITION(["' . implode('", "', $products) . '"], d._id)',
|
||||||
empty($return) ? 'd._key' : $return
|
empty($return) ? 'd._key' : $return
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
'@collection' => static::COLLECTION,
|
'@collection' => static::COLLECTION,
|
||||||
],
|
'language' => $language->name,
|
||||||
|
] + $parameters,
|
||||||
errors: $errors
|
errors: $errors
|
||||||
)) {
|
)) {
|
||||||
// Found parameters
|
// Found parameters
|
||||||
|
|
|
@ -10,6 +10,7 @@ use mirzaev\arming_bot\models\account,
|
||||||
mirzaev\arming_bot\models\enumerations\session as verification,
|
mirzaev\arming_bot\models\enumerations\session as verification,
|
||||||
mirzaev\arming_bot\models\traits\status,
|
mirzaev\arming_bot\models\traits\status,
|
||||||
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
||||||
|
mirzaev\arming_bot\models\traits\buffer,
|
||||||
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;
|
mirzaev\arming_bot\models\enumerations\language;
|
||||||
|
|
||||||
|
@ -33,7 +34,9 @@ use exception;
|
||||||
*/
|
*/
|
||||||
final class session extends core implements arangodb_document_interface
|
final class session extends core implements arangodb_document_interface
|
||||||
{
|
{
|
||||||
use status, arangodb_document_trait;
|
use status, arangodb_document_trait, buffer {
|
||||||
|
buffer::write as write;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the collection in ArangoDB
|
* Name of the collection in ArangoDB
|
||||||
|
@ -43,7 +46,7 @@ final class session extends core implements arangodb_document_interface
|
||||||
/**
|
/**
|
||||||
* Type of sessions verification
|
* Type of sessions verification
|
||||||
*/
|
*/
|
||||||
public const verification VERIFICATION = verification::hash_else_address;
|
final public const verification VERIFICATION = verification::hash_else_address;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor of instance
|
* Constructor of instance
|
||||||
|
@ -325,44 +328,4 @@ final class session extends core implements arangodb_document_interface
|
||||||
// Exit (fail)
|
// Exit (fail)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Write to buffer of the session
|
|
||||||
*
|
|
||||||
* @param array $data Data for merging
|
|
||||||
* @param array &$errors Registry of errors
|
|
||||||
*
|
|
||||||
* @return bool Is data has written into the session document from ArangoDB?
|
|
||||||
*/
|
|
||||||
public function write(array $data, array &$errors = []): bool
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
|
||||||
// Initialized the collection
|
|
||||||
|
|
||||||
// The instance of the session document from ArangoDB is initialized?
|
|
||||||
isset($this->document) || throw new exception('The instance of the sessoin document from ArangoDB is not initialized');
|
|
||||||
|
|
||||||
// Writing data into buffer of the instance of the session document from ArangoDB
|
|
||||||
$this->document->buffer = array_replace_recursive(
|
|
||||||
$this->document->buffer ?? [],
|
|
||||||
[$_SERVER['INTERFACE'] => array_replace_recursive($this->document->buffer[$_SERVER['INTERFACE']] ?? [], $data)]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Writing to ArangoDB and exit (success)
|
|
||||||
return document::update($this->document, errors: $errors);
|
|
||||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
|
||||||
} catch (exception $e) {
|
|
||||||
// Writing to the registry of errors
|
|
||||||
$errors[] = [
|
|
||||||
'text' => $e->getMessage(),
|
|
||||||
'file' => $e->getFile(),
|
|
||||||
'line' => $e->getLine(),
|
|
||||||
'stack' => $e->getTrace()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit (fail)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace mirzaev\arming_bot\models\traits;
|
||||||
|
|
||||||
|
// Files of the project
|
||||||
|
use mirzaev\arming_bot\models\interfaces\collection as collection_interface;
|
||||||
|
|
||||||
|
// Library for ArangoDB
|
||||||
|
use ArangoDBClient\Document as _document;
|
||||||
|
|
||||||
|
// Framework for ArangoDB
|
||||||
|
use mirzaev\arangodb\collection,
|
||||||
|
mirzaev\arangodb\document;
|
||||||
|
|
||||||
|
// Built-in libraries
|
||||||
|
use exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trait for buffer
|
||||||
|
*
|
||||||
|
* @uses collection_interface
|
||||||
|
*
|
||||||
|
* @param static COLLECTION Name of the collection in ArangoDB
|
||||||
|
* @param static TYPE Type of the collection in ArangoDB
|
||||||
|
*
|
||||||
|
* @package mirzaev\arming_bot\models\traits
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
trait buffer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Write to buffer of the session
|
||||||
|
*
|
||||||
|
* @param array $data Data for writing (merge)
|
||||||
|
* @param array &$errors Registry of errors
|
||||||
|
*
|
||||||
|
* @return bool Is data has written into the session document from ArangoDB?
|
||||||
|
*/
|
||||||
|
public function write(array $data, array &$errors = []): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||||
|
// Initialized the collection
|
||||||
|
|
||||||
|
// The instance of the session document from ArangoDB is initialized?
|
||||||
|
isset($this->document) || throw new exception('The instance of the sessoin document from ArangoDB is not initialized');
|
||||||
|
|
||||||
|
// Writing data into buffer of the instance of the session document from ArangoDB
|
||||||
|
$this->document->buffer = array_replace_recursive($this->document->buffer ?? [], $data);
|
||||||
|
|
||||||
|
// Writing to ArangoDB and exit (success)
|
||||||
|
return document::update($this->document, errors: $errors);
|
||||||
|
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||||
|
} catch (exception $e) {
|
||||||
|
// Writing to the registry of errors
|
||||||
|
$errors[] = [
|
||||||
|
'text' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'stack' => $e->getTrace()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit (fail)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,8 @@ use exception;
|
||||||
* @var protected readonly _document|null $document An instance of the ArangoDB document
|
* @var protected readonly _document|null $document An instance of the ArangoDB document
|
||||||
*
|
*
|
||||||
* @package mirzaev\arming_bot\models\traits
|
* @package mirzaev\arming_bot\models\traits
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
trait document
|
trait document
|
||||||
|
|
|
@ -11,6 +11,8 @@ use exception;
|
||||||
* Trait for initialization of files handlers
|
* Trait for initialization of files handlers
|
||||||
*
|
*
|
||||||
* @package mirzaev\arming_bot\models\traits
|
* @package mirzaev\arming_bot\models\traits
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
trait files
|
trait files
|
||||||
|
|
|
@ -11,6 +11,8 @@ use exception;
|
||||||
* Trait for initialization of a status
|
* Trait for initialization of a status
|
||||||
*
|
*
|
||||||
* @package mirzaev\arming_bot\models\traits
|
* @package mirzaev\arming_bot\models\traits
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
trait status
|
trait status
|
||||||
|
|
|
@ -11,6 +11,8 @@ use exception;
|
||||||
* Trait for "Yandex Disk"
|
* Trait for "Yandex Disk"
|
||||||
*
|
*
|
||||||
* @package mirzaev\arming_bot\models\traits\yandex
|
* @package mirzaev\arming_bot\models\traits\yandex
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
trait disk
|
trait disk
|
||||||
|
|
|
@ -39,12 +39,14 @@ $router = new router;
|
||||||
// Initialize routes
|
// Initialize routes
|
||||||
$router
|
$router
|
||||||
->write('/', 'catalog', 'index', 'GET')
|
->write('/', 'catalog', 'index', 'GET')
|
||||||
->write('/search', 'catalog', 'search', 'POST')
|
->write('/', 'catalog', 'index', 'POST')
|
||||||
|
->write('/account/write', 'account', 'write', 'POST')
|
||||||
->write('/session/write', 'session', 'write', 'POST')
|
->write('/session/write', 'session', 'write', 'POST')
|
||||||
->write('/session/connect/telegram', 'session', 'telegram', 'POST')
|
->write('/session/connect/telegram', 'session', 'telegram', 'POST')
|
||||||
->write('/category/$identifier', 'catalog', 'index', 'POST')
|
/* ->write('/category/$identifier', 'catalog', 'index', 'POST') */
|
||||||
->write('/category', 'catalog', 'index', 'POST')
|
/* ->write('/category', 'catalog', 'index', 'POST') */
|
||||||
->write('/product/$identifier', 'catalog', 'product', 'POST');
|
/* ->write('/product/$identifier', 'catalog', 'product', 'POST') */
|
||||||
|
;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,17 @@ import("/js/core.js").then(() =>
|
||||||
if (typeof core.account === "undefined") {
|
if (typeof core.account === "undefined") {
|
||||||
// Not initialized
|
// Not initialized
|
||||||
|
|
||||||
// Write to the core
|
/**
|
||||||
|
* @name Account
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Implements actions with accounts
|
||||||
|
*
|
||||||
|
* @memberof core
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
core.account = class account {
|
core.account = class account {
|
||||||
// Wrap of indicator of the account
|
// Wrap of indicator of the account
|
||||||
static wrap = document.getElementById("account");
|
static wrap = document.getElementById("account");
|
||||||
|
@ -72,10 +82,11 @@ import("/js/core.js").then(() =>
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
if (core.telegram.api.initData.length > 0) {
|
if (core.telegram.api.initData.length > 0) {
|
||||||
core.request(
|
core
|
||||||
"/session/connect/telegram",
|
.request(
|
||||||
core.telegram.api.initData,
|
"/session/connect/telegram",
|
||||||
)
|
core.telegram.api.initData,
|
||||||
|
)
|
||||||
.then((json) => {
|
.then((json) => {
|
||||||
if (
|
if (
|
||||||
json.errors !== null &&
|
json.errors !== null &&
|
||||||
|
@ -109,6 +120,34 @@ import("/js/core.js").then(() =>
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buffer
|
||||||
|
*/
|
||||||
|
static buffer = class buffer {
|
||||||
|
/**
|
||||||
|
* Write to the account buffer
|
||||||
|
*
|
||||||
|
* @param {string} name Name of the parameter
|
||||||
|
* @param {string} value Value of the parameter (it can be JSON)
|
||||||
|
*
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
static write(name, value) {
|
||||||
|
if (typeof name === "string" && typeof value === "string") {
|
||||||
|
//
|
||||||
|
|
||||||
|
// Send request to the server
|
||||||
|
core.request(
|
||||||
|
"/account/write",
|
||||||
|
`${name}=${value}`,
|
||||||
|
"POST",
|
||||||
|
{},
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,20 @@ import("/js/core.js").then(() =>
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
function initialization() {
|
function initialization() {
|
||||||
core.session.telegram();
|
//
|
||||||
|
const { initData, initDataUnsafe, ...data } = core.telegram.api;
|
||||||
|
|
||||||
|
//
|
||||||
|
core.session.buffer.write("telegram_program", JSON.stringify(data));
|
||||||
|
|
||||||
if (core.telegram.api.initData.length > 0) {
|
if (core.telegram.api.initData.length > 0) {
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
core.account.authentication();
|
core.account.authentication();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
core.telegram.api.ready();
|
core.telegram.api.ready();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,7 +22,17 @@ import("/js/core.js").then(() =>
|
||||||
if (typeof core.cart === "undefined") {
|
if (typeof core.cart === "undefined") {
|
||||||
// Not initialized
|
// Not initialized
|
||||||
|
|
||||||
// Write to the core
|
/**
|
||||||
|
* @name Cart
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Implements actions with cart
|
||||||
|
*
|
||||||
|
* @memberof core
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
core.cart = class cart {
|
core.cart = class cart {
|
||||||
/**
|
/**
|
||||||
* Products in cart ["product/148181", "product/148181", "product/148181"...]
|
* Products in cart ["product/148181", "product/148181", "product/148181"...]
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -22,7 +22,17 @@ import("/js/core.js").then(() =>
|
||||||
if (typeof core.connection === "undefined") {
|
if (typeof core.connection === "undefined") {
|
||||||
// Not initialized
|
// Not initialized
|
||||||
|
|
||||||
// Write to the core
|
/**
|
||||||
|
* @name Connection
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Implements actions with websocket connection
|
||||||
|
*
|
||||||
|
* @memberof core
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
core.connection = class connection {
|
core.connection = class connection {
|
||||||
// Wrap of indicator of the connection
|
// Wrap of indicator of the connection
|
||||||
static wrap = document.getElementById("connection");
|
static wrap = document.getElementById("connection");
|
||||||
|
@ -205,7 +215,6 @@ import("/js/core.js").then(() =>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
core.connection.connect(
|
core.connection.connect(
|
||||||
3000,
|
3000,
|
||||||
() =>
|
() =>
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Initialize of the class in global namespace
|
/**
|
||||||
|
* @name Core
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Core of the project
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
const core = class core {
|
const core = class core {
|
||||||
// Domain
|
// Domain
|
||||||
static domain = window.location.hostname;
|
static domain = window.location.hostname;
|
||||||
|
@ -8,31 +16,34 @@ const core = class core {
|
||||||
// Language
|
// Language
|
||||||
static language = "ru";
|
static language = "ru";
|
||||||
|
|
||||||
// Label for the "loading" element
|
// Window
|
||||||
|
static window;
|
||||||
|
|
||||||
|
// The "loading" element
|
||||||
static status_loading = document.getElementById("loading");
|
static status_loading = document.getElementById("loading");
|
||||||
|
|
||||||
// Label for the "account" element
|
// The "account" element
|
||||||
static status_account = document.getElementById("account");
|
static status_account = document.getElementById("account");
|
||||||
|
|
||||||
// Label for the <header> element
|
// The <header> element
|
||||||
static header = document.body.getElementsByTagName("header")[0];
|
static header = document.body.getElementsByTagName("header")[0];
|
||||||
|
|
||||||
// Label for the <aside> element
|
// The <aside> element
|
||||||
static aside = document.body.getElementsByTagName("aside")[0];
|
static aside = document.body.getElementsByTagName("aside")[0];
|
||||||
|
|
||||||
// Label for the "menu" element
|
// The <menu> element
|
||||||
static menu = document.body.querySelector("section[data-section='menu']");
|
static menu = document.body.getElementsByTagName("menu")[0];
|
||||||
|
|
||||||
// Label for the <main> element
|
// The <main> element
|
||||||
static main = document.body.getElementsByTagName("main")[0];
|
static main = document.body.getElementsByTagName("main")[0];
|
||||||
|
|
||||||
// Label for the <footer> element
|
// The <footer> element
|
||||||
static footer = document.body.getElementsByTagName("footer")[0];
|
static footer = document.body.getElementsByTagName("footer")[0];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request
|
* Request
|
||||||
*
|
*
|
||||||
* @param {string} address
|
* @param {string} uri
|
||||||
* @param {string} body
|
* @param {string} body
|
||||||
* @param {string} method POST, GET...
|
* @param {string} method POST, GET...
|
||||||
* @param {object} headers
|
* @param {object} headers
|
||||||
|
@ -41,18 +52,48 @@ const core = class core {
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
static async request(
|
static async request(
|
||||||
address = "/",
|
uri = "/",
|
||||||
body,
|
body,
|
||||||
method = "POST",
|
method = "POST",
|
||||||
headers = {
|
headers = {
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
"Accept": "application/json",
|
||||||
},
|
},
|
||||||
type = "json",
|
type = "json",
|
||||||
) {
|
) {
|
||||||
return await fetch(encodeURI(address), { method, headers, body })
|
return await fetch(encodeURI(uri), { method, headers, body })
|
||||||
.then((response) => response[type]());
|
.then((response) => type === null || response[type]());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buffer
|
||||||
|
*/
|
||||||
|
static buffer = class buffer {
|
||||||
|
/**
|
||||||
|
* Write to buffers
|
||||||
|
*
|
||||||
|
* @param {string} name Name of the parameter
|
||||||
|
* @param {string} value Value of the parameter (it can be JSON)
|
||||||
|
*
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
static write(name, value) {
|
||||||
|
if (typeof this.session === "function") {
|
||||||
|
// Initialized the session implement object
|
||||||
|
|
||||||
|
// Write to the session buffer
|
||||||
|
core.session.buffer.write(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof this.account === "function") {
|
||||||
|
// Initialized the account implement object
|
||||||
|
|
||||||
|
// Write to the account buffer
|
||||||
|
core.account.buffer.write(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Сгенерировать окно выбора действия
|
* Сгенерировать окно выбора действия
|
||||||
*
|
*
|
||||||
|
|
|
@ -19,14 +19,19 @@ import("/js/core.js").then(() => {
|
||||||
// Not initialized
|
// Not initialized
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Damper
|
* @name Damper
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Execute multiple "function" calls in a "timeout" amount of time just once
|
||||||
*
|
*
|
||||||
* @param {function} function Function to execute after damping
|
* @param {function} function Function to execute after damping
|
||||||
* @param {number} timeout Timer in milliseconds (ms)
|
* @param {number} timeout Timer in milliseconds (ms)
|
||||||
* @param {number} force Argument number storing the status of enforcement execution (see @example)
|
* @param {number} force Argument number storing the status of enforcement execution (see @example)
|
||||||
*
|
*
|
||||||
|
* @memberof core
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* $a = damper(
|
* a = damper(
|
||||||
* async (
|
* async (
|
||||||
* a, // 0
|
* a, // 0
|
||||||
* b, // 1
|
* b, // 1
|
||||||
|
@ -40,9 +45,10 @@ import("/js/core.js").then(() => {
|
||||||
* 3, // 3 -> "force" argument
|
* 3, // 3 -> "force" argument
|
||||||
* );
|
* );
|
||||||
*
|
*
|
||||||
* $a('for a', 'for b', 'for c', true, 'for d'); // Force execute is enabled
|
* a('for a', 'for b', 'for c', true, 'for d'); // Force execute is enabled
|
||||||
*
|
*
|
||||||
* @return {void}
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
core.damper = (func, timeout = 300, force) => {
|
core.damper = (func, timeout = 300, force) => {
|
||||||
// Initializing of the timer
|
// Initializing of the timer
|
||||||
|
@ -60,9 +66,7 @@ import("/js/core.js").then(() => {
|
||||||
// Normal execution
|
// Normal execution
|
||||||
|
|
||||||
// Execute the handled function (entry into recursion)
|
// Execute the handled function (entry into recursion)
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => func.apply(this, args), timeout);
|
||||||
func.apply(this, args);
|
|
||||||
}, timeout);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,7 +19,7 @@ import("/js/core.js").then(() => {
|
||||||
// Not initialized
|
// Not initialized
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Бегущая строка
|
* @name Бегущая строка
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Простой, но мощный класс для создания бегущих строк. Поддерживает
|
* Простой, но мощный класс для создания бегущих строк. Поддерживает
|
||||||
|
@ -31,6 +31,8 @@ import("/js/core.js").then(() => {
|
||||||
* события при выбранных действиях для того, чтобы пользователь имел возможность
|
* события при выбранных действиях для того, чтобы пользователь имел возможность
|
||||||
* дорабатывать функционал без изучения и изменения моего кода
|
* дорабатывать функционал без изучения и изменения моего кода
|
||||||
*
|
*
|
||||||
|
* @memberof core
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* сonst hotline = new hotline();
|
* сonst hotline = new hotline();
|
||||||
* hotline.step = '-5';
|
* hotline.step = '-5';
|
||||||
|
@ -41,7 +43,7 @@ import("/js/core.js").then(() => {
|
||||||
* Сейчас при БЫСТРОМ прокручивании можно заметит как элементы "появляются" в начале и конце строки.
|
* Сейчас при БЫСТРОМ прокручивании можно заметит как элементы "появляются" в начале и конце строки.
|
||||||
* 2. "gap" and "padding" in wrap should be removed! or added here to the calculations
|
* 2. "gap" and "padding" in wrap should be removed! or added here to the calculations
|
||||||
*
|
*
|
||||||
* @copyright WTFPL
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
core.hotline = class hotline {
|
core.hotline = class hotline {
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Import dependencies
|
||||||
|
import("/js/core.js").then(() =>
|
||||||
|
import("/js/damper.js").then(() => {
|
||||||
|
const dependencies = setInterval(() => {
|
||||||
|
if (typeof core === "function" && typeof core.damper === "function") {
|
||||||
|
clearInterval(dependencies);
|
||||||
|
clearTimeout(timeout);
|
||||||
|
initialization();
|
||||||
|
}
|
||||||
|
}, 10);
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
clearInterval(dependencies);
|
||||||
|
initialization();
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
function initialization() {
|
||||||
|
if (typeof core.loader === "undefined") {
|
||||||
|
// Not initialized
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Loader
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Implements actions with loading and rendering content
|
||||||
|
*
|
||||||
|
* @memberof core
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
core.loader = class loader {
|
||||||
|
/**
|
||||||
|
* Load
|
||||||
|
*
|
||||||
|
* @param {string} uri
|
||||||
|
* @param {string} body
|
||||||
|
*
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
static async load(uri = "/", body) {
|
||||||
|
if (typeof uri === "string") {
|
||||||
|
// Received and validated uri
|
||||||
|
|
||||||
|
return await core
|
||||||
|
.request(
|
||||||
|
uri,
|
||||||
|
body,
|
||||||
|
"POST",
|
||||||
|
{
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
"json"
|
||||||
|
)
|
||||||
|
.then((json) => {
|
||||||
|
if (
|
||||||
|
json.errors !== null &&
|
||||||
|
typeof json.errors === "object" &&
|
||||||
|
json.errors.length > 0
|
||||||
|
) {
|
||||||
|
// Fail (received errors)
|
||||||
|
} else {
|
||||||
|
// Success (not received errors)
|
||||||
|
|
||||||
|
// Writing to the browser history
|
||||||
|
history.pushState({}, json.title ?? uri, uri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The <title>
|
||||||
|
*
|
||||||
|
* The title of the page
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
typeof json.title === "string" &&
|
||||||
|
json.title.length > 0
|
||||||
|
) {
|
||||||
|
// Received text for the <title> of the page
|
||||||
|
|
||||||
|
// Search for the <title> element (document.title)
|
||||||
|
const title = document.getElementsByTagName("title")[0];
|
||||||
|
|
||||||
|
if (title instanceof HTMLElement) {
|
||||||
|
// Found the <title> element
|
||||||
|
|
||||||
|
// Writing into the <title> element
|
||||||
|
title.innerText = json.title;
|
||||||
|
} else {
|
||||||
|
// Not found the <title> element
|
||||||
|
|
||||||
|
// Initialize the <title> element
|
||||||
|
const title = document.createElement("title");
|
||||||
|
|
||||||
|
// Inititalize the <head> element (document.head)
|
||||||
|
const head = document.getElementsByTagName("head")[0];
|
||||||
|
|
||||||
|
if (head instanceof HTMLElement) {
|
||||||
|
// Found the <head> element
|
||||||
|
|
||||||
|
// Writing the <title> element into the <head> element
|
||||||
|
head.appendChild(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writing title into the <title> element
|
||||||
|
title.innerText = json.title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The <header> element
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
typeof json.header === "string" &&
|
||||||
|
json.header.length > 0
|
||||||
|
) {
|
||||||
|
// Received and validated the header HTML-code
|
||||||
|
|
||||||
|
if (core.header instanceof HTMLElement) {
|
||||||
|
// Found the <header> element
|
||||||
|
|
||||||
|
// Writing into the <header> element
|
||||||
|
core.header.outerHTML = json.header;
|
||||||
|
|
||||||
|
// Reinitializing the parameter with the <header> element
|
||||||
|
core.header =
|
||||||
|
document.getElementsByTagName("header")[0];
|
||||||
|
} else {
|
||||||
|
// Not found the <header> element
|
||||||
|
|
||||||
|
// Initialize the <header> element
|
||||||
|
core.header = document.createElement("header");
|
||||||
|
|
||||||
|
// Inititalize the <body> element (document.body)
|
||||||
|
const body = document.getElementsByTagName("body")[0];
|
||||||
|
|
||||||
|
if (body instanceof HTMLElement) {
|
||||||
|
// Found the <body> element
|
||||||
|
|
||||||
|
if (core.main instanceof HTMLElement) {
|
||||||
|
// Found the <main> element
|
||||||
|
|
||||||
|
// Writing the <header> element before the <main> element
|
||||||
|
body.insertBefore(core.header, core.main);
|
||||||
|
} else if (core.footer instanceof HTMLElement) {
|
||||||
|
// Fount the <footer> element
|
||||||
|
|
||||||
|
// Writing the <header> element before the <footer> element
|
||||||
|
body.insertBefore(core.header, core.footer);
|
||||||
|
} else {
|
||||||
|
// Not found the <main> element and the <footer> element
|
||||||
|
|
||||||
|
// Search for the last <section> element inside the <body> element
|
||||||
|
const section = document.body.querySelector(
|
||||||
|
"body > section:last-of-type"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (section instanceof HTMLElement) {
|
||||||
|
// Found the last <section> element inside the <body> element
|
||||||
|
|
||||||
|
// Writing the <header> element after the last <section> element inside the <body> element
|
||||||
|
body.insertBefore(
|
||||||
|
core.header,
|
||||||
|
section.nextSibling
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Not found section elements <section> inside the <body> element
|
||||||
|
|
||||||
|
// Writing the <header> element into the <body> element
|
||||||
|
body.appendChild(core.header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writing into the <header> element
|
||||||
|
core.header.outerHTML = json.header;
|
||||||
|
|
||||||
|
// Reinitializing the parameter with the <header> element
|
||||||
|
core.header =
|
||||||
|
document.getElementsByTagName("header")[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The <main> element
|
||||||
|
*
|
||||||
|
* The main content of the page
|
||||||
|
*/
|
||||||
|
if (typeof json.main === "string" && json.main.length > 0) {
|
||||||
|
// Received and validated the <main> HTML-code
|
||||||
|
|
||||||
|
if (core.main instanceof HTMLElement) {
|
||||||
|
// Found the <main> element
|
||||||
|
|
||||||
|
// Writing into the <main> element
|
||||||
|
core.main.outerHTML = json.main;
|
||||||
|
|
||||||
|
// Reinitializing the parameter with the <main> element
|
||||||
|
core.main = document.getElementsByTagName("main")[0];
|
||||||
|
} else {
|
||||||
|
// Not found the <main> element
|
||||||
|
|
||||||
|
// Initialize the <main> element
|
||||||
|
core.main = document.createElement("main");
|
||||||
|
|
||||||
|
// Inititalize the <body> element (document.body)
|
||||||
|
const body = document.getElementsByTagName("body")[0];
|
||||||
|
|
||||||
|
if (body instanceof HTMLElement) {
|
||||||
|
// Found the <body> element
|
||||||
|
|
||||||
|
if (core.header instanceof HTMLElement) {
|
||||||
|
// Found the <header> element
|
||||||
|
|
||||||
|
// Writing the <main> element after the <header> element
|
||||||
|
body.insertBefore(
|
||||||
|
core.main,
|
||||||
|
core.header.nextSibling
|
||||||
|
);
|
||||||
|
} else if (core.footer instanceof HTMLElement) {
|
||||||
|
// Fount the <footer> element
|
||||||
|
|
||||||
|
// Writing the <main> element before the <footer> element
|
||||||
|
body.insertBefore(core.main, core.footer);
|
||||||
|
} else {
|
||||||
|
// Not found the <header> element and the <footer> element
|
||||||
|
|
||||||
|
// Search for the last <section> element inside the <body> element
|
||||||
|
const section = document.body.querySelector(
|
||||||
|
"body > section:last-of-type"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (section instanceof HTMLElement) {
|
||||||
|
// Found the last <section> element inside the <body> element
|
||||||
|
|
||||||
|
// Writing the <main> element after the last <section> element inside the <body> element
|
||||||
|
body.insertBefore(core.main, section.nextSibling);
|
||||||
|
} else {
|
||||||
|
// Not found section elements <section> inside the <body> element
|
||||||
|
|
||||||
|
// Writing the <main> element into the <body> element
|
||||||
|
body.appendChild(core.main);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writing into the <main> element
|
||||||
|
core.main.outerHTML = json.main;
|
||||||
|
|
||||||
|
// Reinitializing the parameter with the <main> element
|
||||||
|
core.main = document.getElementsByTagName("main")[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
|
@ -24,22 +24,45 @@ import("/js/core.js").then(() =>
|
||||||
if (typeof core.session === "undefined") {
|
if (typeof core.session === "undefined") {
|
||||||
// Not initialized
|
// Not initialized
|
||||||
|
|
||||||
// Write to the core
|
/**
|
||||||
|
* @name Session
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Implements actions with sessions
|
||||||
|
*
|
||||||
|
* @memberof core
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
core.session = class session {
|
core.session = class session {
|
||||||
/**
|
/**
|
||||||
* Send data of Telegram program settings to the session
|
* Buffer
|
||||||
*
|
|
||||||
* @return {void}
|
|
||||||
*/
|
*/
|
||||||
static telegram() {
|
static buffer = class buffer {
|
||||||
//
|
/**
|
||||||
const { initData, initDataUnsafe, ...data } = core.telegram.api;
|
* Write to the session buffer
|
||||||
|
*
|
||||||
|
* @param {string} name Name of the parameter
|
||||||
|
* @param {string} value Value of the parameter (it can be JSON)
|
||||||
|
*
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
static write(name, value) {
|
||||||
|
if (typeof name === "string" && typeof value === "string") {
|
||||||
|
//
|
||||||
|
|
||||||
core.request(
|
// Send request to the server
|
||||||
"/session/write",
|
core.request(
|
||||||
"telegram=" + JSON.stringify(data),
|
"/session/write",
|
||||||
);
|
`${name}=${value}`,
|
||||||
}
|
"POST",
|
||||||
|
{},
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,20 @@ import("/js/core.js").then(() =>
|
||||||
if (typeof core.telegram === "undefined") {
|
if (typeof core.telegram === "undefined") {
|
||||||
// Not initialized
|
// Not initialized
|
||||||
|
|
||||||
// Write to the core
|
/**
|
||||||
|
* @name Telegram
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Implements actions with data of the telegram account
|
||||||
|
*
|
||||||
|
* @memberof core
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
core.telegram = class telegram {
|
core.telegram = class telegram {
|
||||||
/**
|
/**
|
||||||
* Telegram WebApp API
|
* Telegram "WebApp" API
|
||||||
*
|
*
|
||||||
* @see {@link https://core.telegram.org/bots/webapps#initializing-mini-apps}
|
* @see {@link https://core.telegram.org/bots/webapps#initializing-mini-apps}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
main>section:is(#products, #categories) {
|
||||||
|
width: var(--width);
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
gap: var(--gap, 5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#categories>a.category[type="button"] {
|
||||||
|
--padding: 0.7rem;
|
||||||
|
position: relative;
|
||||||
|
height: 23px;
|
||||||
|
padding: var(--padding);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
color: var(--tg-theme-button-text-color);
|
||||||
|
background-color: var(--tg-theme-button-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main>section#categories:last-child {
|
||||||
|
/* margin-bottom: unset; */
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#categories>a.category[type="button"]:has(>img) {
|
||||||
|
min-width: calc(50% - var(--padding) * 2);
|
||||||
|
height: 180px;
|
||||||
|
padding: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#categories>a.category[type="button"]>img {
|
||||||
|
position: absolute;
|
||||||
|
left: -5%;
|
||||||
|
top: -5%;
|
||||||
|
width: 110%;
|
||||||
|
height: 110%;
|
||||||
|
object-fit: cover;
|
||||||
|
/* filter: blur(1px); */
|
||||||
|
filter: brightness(60%);
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#categories>a.category[type="button"]:is(:hover, :focus)>img {
|
||||||
|
filter: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#categories>a.category[type="button"]:has(>img)>p {
|
||||||
|
position: absolute;
|
||||||
|
left: var(--padding);
|
||||||
|
bottom: var(--padding);
|
||||||
|
right: var(--padding);
|
||||||
|
margin: unset;
|
||||||
|
width: min-content;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
background: var(--tg-theme-secondary-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#categories>a.category[type="button"]>p {
|
||||||
|
z-index: 100;
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#filters {
|
||||||
|
width: var(--width);
|
||||||
|
max-height: 2.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products {
|
||||||
|
--column: calc((100% - var(--gap)) / 2);
|
||||||
|
width: var(--width);
|
||||||
|
display: grid;
|
||||||
|
grid-gap: var(--gap);
|
||||||
|
grid-template-columns: repeat(2, var(--column));
|
||||||
|
grid-auto-flow: row dense;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products>div.column {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--gap);
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products>div.column>article.product {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
overflow: clip;
|
||||||
|
backdrop-filter: brightness(0.7);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products>div.column>article.product:is(:hover, :focus) {
|
||||||
|
/* flex-grow: 0.1; */
|
||||||
|
/* background-color: var(--tg-theme-secondary-bg-color); */
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products>div.column>article.product:is(:hover, :focus)>* {
|
||||||
|
transition: 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products>div.column>article.product:not(:is(:hover, :focus))>* {
|
||||||
|
transition: 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products>div.column>article.product>a {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products>div.column>article.product>a>img:first-of-type {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products>div.column>article.product>a>img:first-of-type+* {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products>div.column>article.product>a>p.title {
|
||||||
|
z-index: 50;
|
||||||
|
margin: unset;
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: bold;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
hyphens: auto;
|
||||||
|
color: var(--tg-theme-text-color);
|
||||||
|
background-color: var(--tg-theme-secondary-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products>div.column>article.product>button:last-of-type {
|
||||||
|
z-index: 100;
|
||||||
|
height: 33px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--tg-theme-button-text-color);
|
||||||
|
background-color: var(--tg-theme-button-color);
|
||||||
|
}
|
|
@ -1,160 +0,0 @@
|
||||||
@charset "UTF-8";
|
|
||||||
|
|
||||||
main>section[data-section="catalog"] {
|
|
||||||
width: var(--width);
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
gap: var(--gap, 5px);
|
|
||||||
/* justify-content: space-between; */
|
|
||||||
/* justify-content: space-evenly; */
|
|
||||||
/* background-color: var(--tg-theme-secondary-bg-color); */
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]:last-child {
|
|
||||||
margin-bottom: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>a.category[type="button"] {
|
|
||||||
height: 23px;
|
|
||||||
padding: 8px 16px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-grow: 1;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
color: var(--tg-theme-button-text-color);
|
|
||||||
background-color: var(--tg-theme-button-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product {
|
|
||||||
/* --product-height: 200px; */
|
|
||||||
--product-height: 220px;
|
|
||||||
--title-font-size: 0.9rem;
|
|
||||||
--title-height: 1.5rem;
|
|
||||||
--button-height: 33px;
|
|
||||||
position: relative;
|
|
||||||
/* width: calc((100% - var(--gap) * 2) / 3); */
|
|
||||||
width: calc((100% - var(--gap)) / 2);
|
|
||||||
height: var(--product-height);
|
|
||||||
display: flex;
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-direction: column;
|
|
||||||
white-space: nowrap;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
overflow: clip;
|
|
||||||
backdrop-filter: brightness(0.7);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product:hover {
|
|
||||||
/* flex-grow: 0.1; */
|
|
||||||
/* background-color: var(--tg-theme-secondary-bg-color); */
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product:hover>* {
|
|
||||||
transition: 0s;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product:not(:hover)>* {
|
|
||||||
transition: 0.2s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product>img:first-of-type {
|
|
||||||
z-index: -50;
|
|
||||||
position: absolute;
|
|
||||||
bottom: var(--button-height);
|
|
||||||
/* bottom: calc(var(--button-height) + var(--title-height)); */
|
|
||||||
width: 100%;
|
|
||||||
/* height: 100%; */
|
|
||||||
height: calc(var(--product-height) - var(--button-height));
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product>img:first-of-type+* {
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product>a {
|
|
||||||
padding: 4px 8px 4px 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
font-weight: bold;
|
|
||||||
backdrop-filter: brightness(0.4) contrast(1.2);
|
|
||||||
color: var(--text-light, var(--tg-theme-text-color));
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product>a.title {
|
|
||||||
padding: 0 8px 0 8px;
|
|
||||||
height: var(--title-height);
|
|
||||||
min-height: var(--title-height);
|
|
||||||
line-height: var(--title-height);
|
|
||||||
font-size: var(--title-font-size);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product:hover>a.title {
|
|
||||||
backdrop-filter: brightness(0.35) contrast(1.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product>a+ :is(a, small) {
|
|
||||||
padding: 0px 8px 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product>small {
|
|
||||||
padding: 3px 8px 0 8px;
|
|
||||||
white-space: normal;
|
|
||||||
word-break: break-all;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
backdrop-filter: brightness(0.4) contrast(1.2);
|
|
||||||
color: var(--text-light, var(--tg-theme-text-color));
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product>small.description {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product:hover>small.description {
|
|
||||||
height: calc(var(--product-height) - var(--title-height) - var(--button-height) - 6px);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product:not(:hover)>small.description {
|
|
||||||
height: 0;
|
|
||||||
padding: 0 8px 0px 8px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product>*:has(+ button:last-of-type) {
|
|
||||||
--offset-before-button: 9px;
|
|
||||||
padding: 4px 8px 13px 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"]>article.product>button:last-of-type {
|
|
||||||
height: var(--button-height);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--tg-theme-button-text-color);
|
|
||||||
background-color: var(--tg-theme-button-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="cart"] {
|
|
||||||
--diameter: 4rem;
|
|
||||||
z-index: 999;
|
|
||||||
right: 5vw;
|
|
||||||
bottom: 5vw;
|
|
||||||
position: fixed;
|
|
||||||
width: var(--diameter);
|
|
||||||
height: var(--diameter);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 100%;
|
|
||||||
background-color: var(--tg-theme-button-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="cart"]>i.icon.shopping.cart {
|
|
||||||
top: -1px;
|
|
||||||
color: var(--tg-theme-button-text-color);
|
|
||||||
}
|
|
|
@ -1,169 +0,0 @@
|
||||||
@charset "UTF-8";
|
|
||||||
|
|
||||||
main>section[data-section="catalog"] {
|
|
||||||
width: var(--width);
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
gap: var(--gap, 5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"] {
|
|
||||||
--padding: 0.7rem;
|
|
||||||
position: relative;
|
|
||||||
height: 23px;
|
|
||||||
padding: var(--padding);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-grow: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
color: var(--tg-theme-button-text-color);
|
|
||||||
background-color: var(--tg-theme-button-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="categories"]:last-child {
|
|
||||||
/* margin-bottom: unset; */
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
position: absolute;
|
|
||||||
left: -5%;
|
|
||||||
top: -5%;
|
|
||||||
width: 110%;
|
|
||||||
height: 110%;
|
|
||||||
object-fit: cover;
|
|
||||||
/* filter: blur(1px); */
|
|
||||||
filter: brightness(60%);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]:is(:hover, :focus)>img {
|
|
||||||
filter: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]:has(>img)>p {
|
|
||||||
position: absolute;
|
|
||||||
left: var(--padding);
|
|
||||||
bottom: var(--padding);
|
|
||||||
right: var(--padding);
|
|
||||||
margin: unset;
|
|
||||||
width: min-content;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
background: var(--tg-theme-secondary-bg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]>p {
|
|
||||||
z-index: 100;
|
|
||||||
padding: 8px 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"] {
|
|
||||||
--column: calc((100% - var(--gap)) / 2);
|
|
||||||
width: var(--width);
|
|
||||||
display: grid;
|
|
||||||
grid-gap: var(--gap);
|
|
||||||
grid-template-columns: repeat(2, var(--column));
|
|
||||||
grid-auto-flow: row dense;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--gap);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-direction: column;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
overflow: clip;
|
|
||||||
backdrop-filter: brightness(0.7);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product:is(:hover, :focus) {
|
|
||||||
/* flex-grow: 0.1; */
|
|
||||||
/* background-color: var(--tg-theme-secondary-bg-color); */
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product:is(:hover, :focus)>* {
|
|
||||||
transition: 0s;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product:not(:is(:hover, :focus))>* {
|
|
||||||
transition: 0.2s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product>a {
|
|
||||||
display: contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product>a>img:first-of-type {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product>a>img:first-of-type+* {
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product>a>p.title {
|
|
||||||
z-index: 50;
|
|
||||||
margin: unset;
|
|
||||||
padding: 4px 8px;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: bold;
|
|
||||||
overflow-wrap: anywhere;
|
|
||||||
hyphens: auto;
|
|
||||||
color: var(--tg-theme-text-color);
|
|
||||||
background-color: var(--tg-theme-secondary-bg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product>button:last-of-type {
|
|
||||||
z-index: 100;
|
|
||||||
height: 33px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--tg-theme-button-text-color);
|
|
||||||
background-color: var(--tg-theme-button-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="cart"] {
|
|
||||||
--diameter: 4rem;
|
|
||||||
z-index: 999;
|
|
||||||
right: 5vw;
|
|
||||||
bottom: 5vw;
|
|
||||||
position: fixed;
|
|
||||||
width: var(--diameter);
|
|
||||||
height: var(--diameter);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 100%;
|
|
||||||
background-color: var(--tg-theme-button-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="cart"]>i.icon.shopping.cart {
|
|
||||||
top: -1px;
|
|
||||||
color: var(--tg-theme-button-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="filters"] {
|
|
||||||
max-height: 2.5rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: start;
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
@charset "UTF-8";
|
|
||||||
|
|
||||||
main>section[data-section="catalog"] {
|
|
||||||
width: var(--width);
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
gap: var(--gap, 5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"] {
|
|
||||||
height: 23px;
|
|
||||||
padding: 8px 16px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-grow: 1;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
color: var(--tg-theme-button-text-color);
|
|
||||||
background-color: var(--tg-theme-button-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="categories"]:last-child {
|
|
||||||
/* margin-bottom: unset; */
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"] {
|
|
||||||
--column: calc((100% - var(--gap) * 2) / 3);
|
|
||||||
width: var(--width);
|
|
||||||
display: grid;
|
|
||||||
grid-gap: var(--gap);
|
|
||||||
grid-template-columns: repeat(3, var(--column));
|
|
||||||
grid-auto-flow: row dense;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--gap);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-direction: column;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
overflow: clip;
|
|
||||||
backdrop-filter: brightness(0.7);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product:hover {
|
|
||||||
/* flex-grow: 0.1; */
|
|
||||||
/* background-color: var(--tg-theme-secondary-bg-color); */
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product:hover>* {
|
|
||||||
transition: 0s;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product:not(:hover)>* {
|
|
||||||
transition: 0.2s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product>img:first-of-type {
|
|
||||||
z-index: -50;
|
|
||||||
width: 100%;
|
|
||||||
max-height: 120px;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product>img:first-of-type+* {
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product>a.title {
|
|
||||||
padding: 4px 8px;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: bold;
|
|
||||||
word-break: break-word;
|
|
||||||
hyphens: auto;
|
|
||||||
color: var(--tg-theme-text-color);
|
|
||||||
background-color: var(--tg-theme-secondary-bg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="products"]>div.column>article.product>button:last-of-type {
|
|
||||||
height: 33px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--tg-theme-button-text-color);
|
|
||||||
background-color: var(--tg-theme-button-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="cart"] {
|
|
||||||
--diameter: 4rem;
|
|
||||||
z-index: 999;
|
|
||||||
right: 5vw;
|
|
||||||
bottom: 5vw;
|
|
||||||
position: fixed;
|
|
||||||
width: var(--diameter);
|
|
||||||
height: var(--diameter);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 100%;
|
|
||||||
background-color: var(--tg-theme-button-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section[data-section="cart"]>i.icon.shopping.cart {
|
|
||||||
top: -1px;
|
|
||||||
color: var(--tg-theme-button-text-color);
|
|
||||||
}
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
i.icon.arrow.top.left {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
border: 2px solid;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.icon.arrow.top.left::after,
|
||||||
|
i.icon.arrow.top.left::before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.icon.arrow.top.left::after {
|
||||||
|
width: 10px;
|
||||||
|
height: 2px;
|
||||||
|
background: currentColor;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
bottom: 8px;
|
||||||
|
right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.icon.arrow.top.left::before {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
left: 4px;
|
||||||
|
top: 4px;
|
||||||
|
border-top: 2px solid;
|
||||||
|
border-left: 2px solid;
|
||||||
|
}
|
|
@ -4,7 +4,6 @@ i.icon.shopping.cart {
|
||||||
display: block;
|
display: block;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
transform: scale(var(--ggs, 1));
|
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 21px;
|
height: 21px;
|
||||||
background:
|
background:
|
||||||
|
|
|
@ -25,6 +25,9 @@ a {
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
--gap: 16px;
|
||||||
|
--width: calc(100% - var(--gap) * 2);
|
||||||
|
--offset-x: 2%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -39,10 +42,20 @@ body {
|
||||||
|
|
||||||
aside {}
|
aside {}
|
||||||
|
|
||||||
header {}
|
header {
|
||||||
|
container-type: inline-size;
|
||||||
|
container-name: header;
|
||||||
|
margin: 2rem 0 1rem;
|
||||||
|
padding: 0 var(--offset-x);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
--offset-x: 2%;
|
container-type: inline-size;
|
||||||
|
container-name: main;
|
||||||
padding: 0 var(--offset-x);
|
padding: 0 var(--offset-x);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -56,8 +69,6 @@ main>section:last-child {
|
||||||
}
|
}
|
||||||
|
|
||||||
main>*[data-section] {
|
main>*[data-section] {
|
||||||
--gap: 16px;
|
|
||||||
--width: calc(100% - var(--gap) * 2);
|
|
||||||
width: var(--width);
|
width: var(--width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +161,7 @@ a[type="button"] {
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2 {
|
h2 {
|
||||||
margin: 28px 0 0;
|
margin: 1rem 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {}
|
footer {}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
header>nav#menu {
|
||||||
|
container-type: inline-size;
|
||||||
|
container-name: menu;
|
||||||
|
width: var(--width);
|
||||||
|
min-height: 3rem;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
gap: 1rem;
|
||||||
|
border-radius: 1.375rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
header>nav#menu>a[type="button"] {
|
||||||
|
height: 3rem;
|
||||||
|
padding: unset;
|
||||||
|
border-radius: 1.375rem;
|
||||||
|
color: var(--unsafe-color, var(--tg-theme-button-text-color));
|
||||||
|
background-color: var(--unsafe-background-color, var(--tg-theme-button-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
header>nav#menu>a[type=button]>:first-child {
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
header>nav#menu>a[type="button"]>* {
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@container header (max-width: 450px) {
|
||||||
|
header>nav#menu > a[type="button"]:nth-child(1)> i.icon+span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container header (max-width: 350px) {
|
||||||
|
header>nav#menu > a[type="button"]:nth-child(2)> i.icon+span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container header (max-width: 250px) {
|
||||||
|
header>nav#menu > a[type="button"]:nth-child(3)> i.icon+span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container header (max-width: 150px) {
|
||||||
|
header>nav#menu > a[type="button"]> i.icon+span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
|
@ -67,7 +67,6 @@ final class templater extends controller implements ArrayAccess
|
||||||
if (!empty($account?->status())) $this->twig->addGlobal('account', $account);
|
if (!empty($account?->status())) $this->twig->addGlobal('account', $account);
|
||||||
$this->twig->addGlobal('language', $account?->language->name ?? $settings?->language->name ?? 'en');
|
$this->twig->addGlobal('language', $account?->language->name ?? $settings?->language->name ?? 'en');
|
||||||
|
|
||||||
|
|
||||||
// Initialize function of dimensions formattinx
|
// Initialize function of dimensions formattinx
|
||||||
$this->twig->addFunction(
|
$this->twig->addFunction(
|
||||||
new TwigFunction(
|
new TwigFunction(
|
||||||
|
|
|
@ -19,5 +19,5 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script type="text/javascript" src="/js/account.js"></script>
|
<script src="/js/account.js"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
<section data-section="cart">
|
|
||||||
<i class="icon shopping cart"></i>
|
|
||||||
</section>
|
|
|
@ -1,20 +1,15 @@
|
||||||
{% if categories is not empty %}
|
{% if categories is not empty %}
|
||||||
<section class="unselectable" data-section="catalog" data-catalog-type="categories"
|
<section id="categories" class="unselectable">
|
||||||
data-catalog-level="{{ level ?? 0 }}">
|
|
||||||
{% for category in categories %}
|
{% for category in categories %}
|
||||||
{% if category.images %}
|
<a id="{{ category.getId() }}" class="category" type="button" href="?category={{ category.identifier }}"
|
||||||
<a id="{{ category.getId() }}" class="category" type="button" onclick="core.catalog.category(this)" onkeydown="event.keyCode === 13 && core.catalog.category(this)"
|
onclick="core.catalog.parameters.set('category', {{ category.identifier }}); return core.catalog.search(event);"
|
||||||
data-category-identifier="{{ category.identifier }}" tabindex="3">
|
onkeydown="event.keyCode === 13 && (core.catalog.parameters.set('category', {{ category.identifier }}), core.catalog.search(event))"
|
||||||
<img src="{{ category.images.0.storage }}"
|
tabindex="3">
|
||||||
alt="{{ category.name[language] }}" ondrugstart="return false;">
|
{% if category.images %}
|
||||||
|
<img src="{{ category.images.0.storage }}" alt="{{ category.name[language] }}" ondrugstart="return false;">
|
||||||
|
{% endif %}
|
||||||
<p>{{ category.name[language] }}</p>
|
<p>{{ category.name[language] }}</p>
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
|
||||||
<a id="{{ category.getId() }}" class="category" type="button" onclick="core.catalog.category(this)" onkeydown="event.keyCode === 13 && core.catalog.category(this)"
|
|
||||||
data-category-identifier="{{ category.identifier }}" tabindex="3">
|
|
||||||
<p>{{ category.name[language] }}</p>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -1,13 +1,21 @@
|
||||||
{% if filters is not empty %}
|
{% if filters is not empty %}
|
||||||
<section class="unselectble" data-section="filters">
|
<section id="filters" class="unselectble">
|
||||||
<section class="unselectable" data-type="select" data-filter="brand" tabindex="4">
|
<section class="unselectable" data-type="select" tabindex="4">
|
||||||
<input name="brand" type="radio" id="brand_title"{% if session.filters.brand is empty %} checked{% endif %}>
|
{% set buffer_brand = account.buffer.catalog.filters.brand ?? session.buffer.catalog.filters.brand %}
|
||||||
<label for="brand_title" type="button">{{ language == 'ru' ? 'Бренд' : 'Brand' }}</label>
|
<input name="brand" type="radio" id="brand_title" {% if buffer_brand is empty %} checked{% endif %}>
|
||||||
|
<label for="brand_title" type="button" onclick="core.catalog.parameters.set('brand', null); core.catalog.search(event)">
|
||||||
|
{{ language == 'ru' ? 'Бренд' : 'Brand' }}
|
||||||
|
</label>
|
||||||
<input name="brand" type="radio" id="brand_all">
|
<input name="brand" type="radio" id="brand_all">
|
||||||
<label for="brand_all" type="button">{{ language == 'ru' ? 'Все бренды' : 'All brands' }}</label>
|
<label for="brand_all" type="button" onclick="core.catalog.parameters.set('brand', null); core.catalog.search(event)">
|
||||||
|
{{ language == 'ru' ? 'Все бренды' : 'All brands' }}
|
||||||
|
</label>
|
||||||
{% for brand in filters.brands %}
|
{% for brand in filters.brands %}
|
||||||
<input name="brand" type="radio" id="brand_{{ loop.index }}"{% if session.filters.brand == brand %} checked{% endif %}>
|
<input name="brand" type="radio" id="brand_{{ loop.index }}" {% if brand == buffer_brand %} checked{% endif %}>
|
||||||
<label for="brand_{{ loop.index }}" type="button">{{ brand }}</label>
|
<label for="brand_{{ loop.index }}" type="button"
|
||||||
|
onclick="core.catalog.parameters.set('brand', '{{ brand }}'); core.catalog.search(event)">
|
||||||
|
{{ brand }}
|
||||||
|
</label>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% macro card(product) %}
|
{% macro card(product) %}
|
||||||
{% set title = product.name[language] ~ ' ' ~ product.brand[language] ~ format_dimensions(product.dimensions.x, product.dimensions.y, 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 ~ 'г' %}
|
||||||
<article id="{{ product.getId() }}" class="product unselectable">
|
<article id="{{ product.getId() }}" class="product unselectable">
|
||||||
<a data-product-identifier="{{ product.identifier }}" onclick="core.catalog.product(this)" onkeydown="event.keyCode === 13 && core.catalog.product(this)" tabindex="10">
|
<a data-product-identifier="{{ product.identifier }}" href="?product={{ product.identifier }}" onclick="return core.catalog.product(this);" onkeydown="event.keyCode === 13 && core.catalog.product(this)" tabindex="10">
|
||||||
<img src="{{ product.images.0.storage }}" alt="{{ product.name[language] }}" ondrugstart="return false;">
|
<img src="{{ product.images.0.storage }}" alt="{{ product.name[language] }}" ondrugstart="return false;">
|
||||||
<p class="title" title="{{ product.name[language] }}">
|
<p class="title" title="{{ product.name[language] }}">
|
||||||
{{ title | length > 45 ? title | slice(0, 45) ~ '...' : title }}
|
{{ title | length > 45 ? title | slice(0, 45) ~ '...' : title }}
|
||||||
|
@ -13,14 +13,7 @@
|
||||||
</article>
|
</article>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
{% if products is not empty %}
|
{% if products is not empty %}
|
||||||
<section class="unselectable" data-section="catalog" data-catalog-type="products" data-catalog-level="{{ level ?? 0 }}">
|
<section id="products" class="unselectable">
|
||||||
<div class="column">
|
|
||||||
{% for product in products %}
|
|
||||||
{% if loop.index % 2 == 0 %}
|
|
||||||
{{ _self.card(product) }}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<div class="column">
|
<div class="column">
|
||||||
{% for product in products %}
|
{% for product in products %}
|
||||||
{% if loop.index % 2 == 1 %}
|
{% if loop.index % 2 == 1 %}
|
||||||
|
@ -28,5 +21,12 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
{% for product in products %}
|
||||||
|
{% if loop.index % 2 == 0 %}
|
||||||
|
{{ _self.card(product) }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
|
@ -1,13 +0,0 @@
|
||||||
{% if products is not empty %}
|
|
||||||
<section class="unselectable" data-section="catalog" data-catalog-type="products" data-catalog-level="{{ level ?? 0 }}">
|
|
||||||
{% for product in products %}
|
|
||||||
<article id="{{ product.getId() }}" class="product unselectable">
|
|
||||||
<img src="/images/{{ product.getKey() }}/1.jpg" alt="{{ product.title.ru }}" ondrugstart="return false;" onclick="return core.catalog.product({{ product.getKey() }});">
|
|
||||||
<a class="title" title="{{ product.title.ru }}" onclick="return core.catalog.product({{ product.getKey() }});">{{ product.title.ru }}</a>
|
|
||||||
<small class="description" title="{{ product.description.ru }}" onclick="return core.catalog.product({{ product.getKey() }});">{{ product.description.ru | length > 160 ? product.description.ru | slice(0, 160) ~
|
|
||||||
'...' : product.description.ru }}</small>
|
|
||||||
<button title="Добавить в корзину" onclick="return core.catalog.cart.add({{ product.getKey() }})">{{ product.cost }}р</button>
|
|
||||||
</article>
|
|
||||||
{% endfor %}
|
|
||||||
</section>
|
|
||||||
{% endif %}
|
|
|
@ -1,36 +0,0 @@
|
||||||
{% macro card(product) %}
|
|
||||||
<article id="{{ product.getId() }}" class="product unselectable">
|
|
||||||
<img src="/images/{{ product.getKey() }}/1.jpg" alt="{{ product.title.ru }}" ondrugstart="return false;"
|
|
||||||
onclick="return core.catalog.product({{ product.getKey() }});">
|
|
||||||
<a class="title" title="{{ product.title.ru }}" onclick="return core.catalog.product({{ product.getKey() }});">{{
|
|
||||||
product.title.ru | length > 35 ? product.title.ru | slice(0, 35) ~ '...' : product.title.ru }}</a>
|
|
||||||
<button title="Добавить в корзину" onclick="return core.catalog.cart.add({{ product.getKey() }})">{{ product.cost
|
|
||||||
}}р</button>
|
|
||||||
</article>
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{% if products is not empty %}
|
|
||||||
<section class="unselectable" data-section="catalog" data-catalog-type="products" data-catalog-level="{{ level ?? 0 }}">
|
|
||||||
<div class="column">
|
|
||||||
{% for product in products %}
|
|
||||||
{% if loop.index0 % 3 == 0 %}
|
|
||||||
{{ _self.card(product) }}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<div class="column">
|
|
||||||
{% for product in products %}
|
|
||||||
{% if loop.index0 % 3 == 1 %}
|
|
||||||
{{ _self.card(product) }}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<div class="column">
|
|
||||||
{% for product in products %}
|
|
||||||
{% if loop.index0 % 3 == 2 %}
|
|
||||||
{{ _self.card(product) }}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
{% endif %}
|
|
|
@ -1,4 +1,7 @@
|
||||||
<search data-section="search">
|
<search id="search">
|
||||||
<label><i class="icon search"></i></label>
|
<label class="unselectable"><i class="icon search"></i></label>
|
||||||
<input id="search" placeholder="Поиск по каталогу" type="search" onkeyup="event.keyCode !== 9 && core.catalog.search(event, this)" tabindex="1"/>
|
{% set buffer_search = account.buffer.catalog.search.text ?? session.buffer.catalog.search.text %}
|
||||||
|
<input placeholder="Поиск по каталогу" type="search" tabindex="1"
|
||||||
|
onkeyup="event.keyCode === 9 || core.catalog.parameters.set('text', this.value); core.catalog.search(event, this)"
|
||||||
|
{% if buffer_search is not empty %} value="{{ buffer_search }}" {% endif %} />
|
||||||
</search>
|
</search>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/catalog/2columns.css" />
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/catalog.css" />
|
||||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/interface/select.css" />
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/interface/select.css" />
|
||||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/search.css" />
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/search.css" />
|
||||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/shopping_cart.css" />
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/shopping_cart.css" />
|
||||||
|
@ -10,13 +10,11 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<h2 class="unselectable">Каталог</h2>
|
<h2 class="unselectable">{{ h2 }}</h2>
|
||||||
|
|
||||||
{% include "/themes/default/catalog/elements/search.html" %}
|
{% include "/themes/default/catalog/elements/search.html" %}
|
||||||
{% include "/themes/default/catalog/elements/categories.html" %}
|
{% include "/themes/default/catalog/elements/categories.html" %}
|
||||||
{% include "/themes/default/catalog/elements/filters.html" %}
|
{% include "/themes/default/catalog/elements/filters.html" %}
|
||||||
{% include "/themes/default/catalog/elements/products/2columns.html" %}
|
{% include "/themes/default/catalog/elements/products.html" %}
|
||||||
<!-- {% include "/themes/default/catalog/elements/cart.html" %} -->
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
|
|
|
@ -10,5 +10,5 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script type="text/javascript" src="/js/connection.js"></script>
|
<script src="/js/connection.js"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
{% block title %}
|
{% block title %}
|
||||||
<title>{% if head.title != empty %}{{head.title}}{% else %}ARMING{% endif %}</title>
|
<title>{% if head.title is not empty %}{{ head.title }}{% else %}{{ settings.project.name }}{% endif %}</title>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block meta %}
|
{% block meta %}
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
{% for meta in head.metas %}
|
{% for meta in head.metas %}
|
||||||
<meta {% for name, value in meta.attributes %}{{name}}="{{value}}" {% endfor %}>
|
<meta {% for name, value in meta.attributes %}{{ name }}="{{ value }}" {% endfor %}>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
<!-- <link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/initialization.css" /> -->
|
|
||||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/main.css" />
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/main.css" />
|
||||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/loading_spinner.css" />
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/loading_spinner.css" />
|
||||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/loading.css" />
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/loading.css" />
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
|
{% use "/themes/default/menu.html" with css as menu_css, body as menu_body, js as menu_js %}
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
|
{{ block('menu_css') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<header>
|
<header>
|
||||||
|
{{ block('menu_body') }}
|
||||||
</header>
|
</header>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
|
{{ block('menu_js') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -7,16 +7,16 @@
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
{{ block('header_css') }}
|
|
||||||
{{ block('connection_css') }}
|
{{ block('connection_css') }}
|
||||||
{{ block('account_css') }}
|
{{ block('account_css') }}
|
||||||
|
{{ block('header_css') }}
|
||||||
{{ block('footer_css') }}
|
{{ block('footer_css') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{{ block('header') }}
|
|
||||||
{{ block('connection_body') }}
|
{{ block('connection_body') }}
|
||||||
{{ block('account_body') }}
|
{{ block('account_body') }}
|
||||||
|
{{ block('header') }}
|
||||||
<main>
|
<main>
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{{ main|raw }}
|
{{ main|raw }}
|
||||||
|
@ -27,8 +27,8 @@
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
{{ block('header_js') }}
|
|
||||||
{{ block('connection_js') }}
|
{{ block('connection_js') }}
|
||||||
{{ block('account_js') }}
|
{{ block('account_js') }}
|
||||||
|
{{ block('header_js') }}
|
||||||
{{ block('footer_js') }}
|
{{ block('footer_js') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,8 +1,38 @@
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script src="https://telegram.org/js/telegram-web-app.js" defer></script>
|
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
||||||
<script src="/js/core.js" defer></script>
|
<script src="/js/core.js"></script>
|
||||||
<script src="/js/damper.js"></script>
|
<script src="/js/damper.js"></script>
|
||||||
|
<script src="/js/loader.js"></script>
|
||||||
<script src="/js/telegram.js"></script>
|
<script src="/js/telegram.js"></script>
|
||||||
<script src="/js/session.js"></script>
|
<script src="/js/session.js"></script>
|
||||||
<script src="/js/authentication.js"></script>
|
<script src="/js/authentication.js"></script>
|
||||||
|
{% if javascript is not empty %}
|
||||||
|
<script>
|
||||||
|
import("/js/core.js").then(() =>
|
||||||
|
import("/js/damper.js").then(() => {
|
||||||
|
const dependencies = setInterval(() => {
|
||||||
|
if (
|
||||||
|
typeof core === "function" &&
|
||||||
|
typeof core.damper === "function"
|
||||||
|
) {
|
||||||
|
clearInterval(dependencies);
|
||||||
|
clearTimeout(timeout);
|
||||||
|
initialization();
|
||||||
|
}
|
||||||
|
}, 10);
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
clearInterval(dependencies);
|
||||||
|
initialization();
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
function initialization() {
|
||||||
|
let _window;
|
||||||
|
{% for code in javascript %}
|
||||||
|
{{ code|raw }}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
{% block css %}
|
||||||
|
<link type="text/css" rel="stylesheet" href="/themes/default/css/menu.css">
|
||||||
|
{% for button in menu %}
|
||||||
|
{% if button.icon %}
|
||||||
|
<link type="text/css" rel="stylesheet" href="/themes/default/css/icons/{{ button.icon|replace({' ': '-'}) }}.css">
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<nav id="menu">
|
||||||
|
{% for button in menu %}
|
||||||
|
<a href='{{ button.urn }}' onclick="return core.loader.load('{{ button.urn }}');" type="button" class="unselectable"
|
||||||
|
title="{{ button.name }}"
|
||||||
|
style="order: {{ button.position }};{% for target, color in button.color %} --unsafe-{{ target }}: {{ color|e }};{% endfor %}">
|
||||||
|
{% if button.icon %}
|
||||||
|
<i class="icon {{ button.icon }}"></i>
|
||||||
|
{% endif %}
|
||||||
|
<span>{{ button.name }}</span>
|
||||||
|
{% if button.image.storage %}
|
||||||
|
<img src="{{ button.image.storage }}" alt="{{ button.name }}" ondrugstart="return false;">
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</nav>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
<script src="/js/menu.js"></script>
|
||||||
|
{% endblock %}
|
|
@ -1,14 +0,0 @@
|
||||||
{% extends "/themes/default/index.html" %}
|
|
||||||
|
|
||||||
{% block css %}
|
|
||||||
{{ parent() }}
|
|
||||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/catalog.css" />
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block main %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block js %}
|
|
||||||
{{ parent() }}
|
|
||||||
<script src="/js/catalog.js" defer></script>
|
|
||||||
{% endblock %}
|
|
Loading…
Reference in New Issue