Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
86ad3f5bfc | ||
|
80d3b430ef | ||
|
b3f6f15750 | ||
|
430715c792 | ||
|
ec03c8218c | ||
|
5382e15af2 | ||
|
7847d15da1 | ||
|
10af91a081 |
|
@ -1 +1,2 @@
|
|||
node_modules
|
||||
vendor
|
||||
|
|
117
README.md
117
README.md
|
@ -173,13 +173,24 @@ You can copy an example of server file from here: `/examples/nginx/server.conf`
|
|||
Edit the file `/etc/nginx/mime.types`<br>
|
||||
`application/javascript js;` -> `application/javascript js mjs;`
|
||||
|
||||
3. **Generate a TLS/SSL sertificate** (via [certbot](http://certbot.eff.org/) for [ubuntu](https://ubuntu.com/))<br>
|
||||
3.1. `sudo apt install certbot python3-certbot-nginx`<br>
|
||||
3.2. `sudo certbot certonly --nginx` (The **domain** must already be **bound** to the **IP-address** of the server by `A-record` or `AAAA-record`)
|
||||
|
||||
5. **Firewall rules for HTTP and HTTPS** (for [ubuntu](https://ubuntu.com/))<br>
|
||||
4.1 `sudo ufw allow "NGINX Full"`<br>
|
||||
4.1.1 `sudo ufw allow 22` (make sure that the port for SSH connection is open)<br>
|
||||
4.2 `sudo ufw enable`
|
||||
|
||||
### SystemD (or any alternative you like)
|
||||
You can copy an example of systemd file from here: `/examples/systemd/huesos.service`<br><br>
|
||||
**Execute:** `sudo cp huesos.service /etc/systemd/system/huesos.service && sudo chmod +x /etc/systemd/system/huesos.service`<br><br>
|
||||
You can copy an example of systemd file from here: `/examples/systemd/huesos.service`<br>
|
||||
1. `sudo cp huesos.service /etc/systemd/system/huesos.service && sudo chmod +x /etc/systemd/system/huesos.service`
|
||||
2. `sudo systemctl daemon-reload`
|
||||
3. `sudo systemctl enable huesos`<br>
|
||||
|
||||
*before you execute the command think about **what it does** and whether the **paths** are specified correctly*<br>
|
||||
*the configuration file is very simple and you can remake it for any alternative to SystemD that you like*
|
||||
|
||||
|
||||
## Menu
|
||||
*Menu inside the Web App*<br><br>
|
||||
Make sure you have a **menu** collection (can be created automatically)<br>
|
||||
|
@ -195,7 +206,7 @@ You can copy a clean menu documents without comments from here: `/examples/arang
|
|||
"style": { // The `style` attribute
|
||||
"order": 0
|
||||
},
|
||||
"class": {},
|
||||
"class": "",
|
||||
"icon": { // Icon from `/themes/default/css/icons`
|
||||
"style": { // The `style` attribute
|
||||
"rotate": "-135deg"
|
||||
|
@ -215,9 +226,9 @@ You can copy a clean settings document without comments from here: `/examples/ar
|
|||
|
||||
```json
|
||||
{
|
||||
"status": "active",
|
||||
"status": "active", // Values: "active", "inactive" (string) Status of the settings document?
|
||||
"project": {
|
||||
"name": "PROJECT"
|
||||
"name": "PROJECT" // Name of the projext (string)
|
||||
},
|
||||
"language": "en", // Will be converted to an instance of enumeration `mirzaev\arming_bot\models\enumerations\language`
|
||||
"currency": "usd", // Will be converted to an instance of enumeration `mirzaev\arming_bot\models\enumerations\currency`
|
||||
|
@ -228,7 +239,99 @@ You can copy a clean settings document without comments from here: `/examples/ar
|
|||
"offer": false, // Display the data of a public offer in the footer? (bool) (does not affect the `/offer` page generation)
|
||||
"sim": null, // Examples: "+7 000 000-00-00", "70000000000" (string|null) (if `null` it will not be displayed)
|
||||
"mail": null // Example: "name@domain.zone" (string|null) (if `null` it will not be displayed)
|
||||
},
|
||||
"search": {
|
||||
"enabled": true, // Enable the search input field?
|
||||
"position": "fixed" // Values: "fixed", "relative"
|
||||
},
|
||||
"catalog": {
|
||||
"categories": {
|
||||
"display": "column", // Values: "row" (flex wrap), "column" (rows) Type of the catalog display
|
||||
"structure": "lists", // Values: "pages" (pages with categories and products), "lists" (all categories as tree lists on the main page)
|
||||
"buttons": {
|
||||
"height": "120px", // Examples: "80px", "120px", "180px" (string|null) Height of buttons
|
||||
"background": "#fafafa", // Examples: "#fafafa", "yellow" (string|null) Color of buttons background
|
||||
"separator": {
|
||||
"enabled": true, // Enable separators?
|
||||
"width": "60%" // Exaples: "100%", "80%", "60%" (string|null) Width of separators over images (relative to image width from the left)
|
||||
},
|
||||
"lists": {
|
||||
"height": "800px", // Examples: "500px", "100%" (string|null) Maximum height of lists (`max-height` for animations working)
|
||||
"background": null, // Examples: "#fafafa", "yellow" (string|null) Color of lists
|
||||
"separator": null, // Examples: "#fafafa", "yellow" (string|null) Color of separators between rows
|
||||
"separated": true, // Separate lists from its buttons?
|
||||
"blocks": true, // Blocks instead of plain text?
|
||||
"arrow": true // Add arrow at the right?
|
||||
},
|
||||
"texts": {
|
||||
"position": {
|
||||
"vertical": "center" // Values: "top", "center", "bottom" (string|null) Position of texts of ascendants categories
|
||||
},
|
||||
"width": "max(8rem, 20%)", // Examples: "60%", "5rem", "max(8rem, 20%)" (string|null) Width of the text section (left side of buttons)
|
||||
"background": false, // Enable wrapping element for texts?
|
||||
"title": {
|
||||
"color": "#020202" // Examples: "#fafafa", "yellow" (string|null) Color of titles
|
||||
},
|
||||
"description": {
|
||||
"color": "#121212" // Examples: "#fafafa", "yellow" (string|null) Color of descriptions
|
||||
}
|
||||
},
|
||||
"images": {
|
||||
"filter": "contrast(1.2)" // Example: "contrast(1.2)" (string|null) Filter for images
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cart": {
|
||||
"enabled": true // Enable the cart button?
|
||||
},
|
||||
"css": {
|
||||
"catalog-button-cart-background": "#40a7e3",
|
||||
"product-button-cart-background": "#40a7e3"
|
||||
"catalog-button-cart-added-background": "#90be36",
|
||||
"product-button-cart-added-background": "#90be36"
|
||||
},
|
||||
"account": {
|
||||
"enabled": false // Enable the account section? (works only when opened from telegram `inline-button`)
|
||||
},
|
||||
"menu": {
|
||||
"enabled": true, // Enable the main menu?
|
||||
"position": "fixed" // Values: "fixed" (fixed to the bottom as a solid line), "relative" (at the top as separated buttons) (stirng) Position of the main menu
|
||||
},
|
||||
"header": {
|
||||
"enabled": true, // Enable the header?
|
||||
"position": "fixed" // Values: "fixed" (fixed to the bottom), "relative" (at the top) (stirng) Position of the header
|
||||
},
|
||||
"acquirings": {
|
||||
"robokassa": {
|
||||
"enabled": true, // Enable the Robokassa acquiring?
|
||||
"mode": "test" // Values: "work", "test" (string) Mode of the Robokassa acquiring
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"deliveries": {
|
||||
"site": [], // Values: "sim", "name", "destination", "address", "commentary"
|
||||
"chat": [
|
||||
"sim",
|
||||
"name",
|
||||
"destination",
|
||||
"address",
|
||||
"commentary"
|
||||
] // Values: "sim", "name", "destination", "address", "commentary"
|
||||
}
|
||||
},
|
||||
"deliveries": {
|
||||
"cdek": {
|
||||
"enabled": true, // Enable CDEK delivery?
|
||||
"label": "CDEK" // Name of the CDEK delivery
|
||||
}
|
||||
},
|
||||
"chats": [
|
||||
{
|
||||
"id": null, // Example: -1002599391893 (int) (negative number) The telegram chat identifier
|
||||
"orders": true // Send orders? (for moderators)
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -261,3 +364,5 @@ You can copy a clean suspension document without comments from here: `/examples/
|
|||
*Russian weapons tuning shop* ([repository](https://git.svoboda.works/mirzaev/arming))
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
"require": {
|
||||
"php": "^8.4",
|
||||
"ext-gd": "^8.4",
|
||||
"ext-intl": "^8.4",
|
||||
"triagens/arangodb": "^3.8",
|
||||
"mirzaev/minimal": "^3.4.0",
|
||||
"mirzaev/arangodb": "^2",
|
||||
|
@ -32,7 +33,8 @@
|
|||
"avadim/fast-excel-reader": "^2.19",
|
||||
"ttatpuot/cdek-sdk2.0": "^1.2",
|
||||
"guzzlehttp/guzzle": "^7.9",
|
||||
"php-http/guzzle7-adapter": "^1.0"
|
||||
"php-http/guzzle7-adapter": "^1.0",
|
||||
"react/async": "^4.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "e4632693e41625716be26163a7e06ecc",
|
||||
"content-hash": "0c66c5ef998a3ccf4d0738e41bf2a3d3",
|
||||
"packages": [
|
||||
{
|
||||
"name": "avadim/fast-excel-helper",
|
||||
|
@ -63,16 +63,16 @@
|
|||
},
|
||||
{
|
||||
"name": "avadim/fast-excel-reader",
|
||||
"version": "v2.23.0",
|
||||
"version": "v2.24.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aVadim483/fast-excel-reader.git",
|
||||
"reference": "581ca926d359f18043faec7dc7d8bea654b1d8e4"
|
||||
"reference": "8afafb92817705a6bfd1c9bc53b0083d9d36e7c1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aVadim483/fast-excel-reader/zipball/581ca926d359f18043faec7dc7d8bea654b1d8e4",
|
||||
"reference": "581ca926d359f18043faec7dc7d8bea654b1d8e4",
|
||||
"url": "https://api.github.com/repos/aVadim483/fast-excel-reader/zipball/8afafb92817705a6bfd1c9bc53b0083d9d36e7c1",
|
||||
"reference": "8afafb92817705a6bfd1c9bc53b0083d9d36e7c1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -114,9 +114,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/aVadim483/fast-excel-reader/issues",
|
||||
"source": "https://github.com/aVadim483/fast-excel-reader/tree/v2.23.0"
|
||||
"source": "https://github.com/aVadim483/fast-excel-reader/tree/v2.24.0"
|
||||
},
|
||||
"time": "2025-02-06T13:48:12+00:00"
|
||||
"time": "2025-02-27T13:29:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "badfarm/zanzara",
|
||||
|
@ -2687,6 +2687,81 @@
|
|||
},
|
||||
"time": "2019-03-08T08:55:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "react/async",
|
||||
"version": "v4.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/reactphp/async.git",
|
||||
"reference": "635d50e30844a484495713e8cb8d9e079c0008a5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/reactphp/async/zipball/635d50e30844a484495713e8cb8d9e079c0008a5",
|
||||
"reference": "635d50e30844a484495713e8cb8d9e079c0008a5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"react/event-loop": "^1.2",
|
||||
"react/promise": "^3.2 || ^2.8 || ^1.2.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.10.39",
|
||||
"phpunit/phpunit": "^9.6"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"React\\Async\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Christian Lück",
|
||||
"email": "christian@clue.engineering",
|
||||
"homepage": "https://clue.engineering/"
|
||||
},
|
||||
{
|
||||
"name": "Cees-Jan Kiewiet",
|
||||
"email": "reactphp@ceesjankiewiet.nl",
|
||||
"homepage": "https://wyrihaximus.net/"
|
||||
},
|
||||
{
|
||||
"name": "Jan Sorgalla",
|
||||
"email": "jsorgalla@gmail.com",
|
||||
"homepage": "https://sorgalla.com/"
|
||||
},
|
||||
{
|
||||
"name": "Chris Boden",
|
||||
"email": "cboden@gmail.com",
|
||||
"homepage": "https://cboden.dev/"
|
||||
}
|
||||
],
|
||||
"description": "Async utilities and fibers for ReactPHP",
|
||||
"keywords": [
|
||||
"async",
|
||||
"reactphp"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/reactphp/async/issues",
|
||||
"source": "https://github.com/reactphp/async/tree/v4.3.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://opencollective.com/reactphp",
|
||||
"type": "open_collective"
|
||||
}
|
||||
],
|
||||
"time": "2024-06-04T14:40:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "react/cache",
|
||||
"version": "v1.2.0",
|
||||
|
@ -3585,16 +3660,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/cache",
|
||||
"version": "v7.2.3",
|
||||
"version": "v7.2.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/cache.git",
|
||||
"reference": "8d773a575e446de220dca03d600b2d8e1c1c10ec"
|
||||
"reference": "d33cd9e14326e14a4145c21e600602eaf17cc9e7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/cache/zipball/8d773a575e446de220dca03d600b2d8e1c1c10ec",
|
||||
"reference": "8d773a575e446de220dca03d600b2d8e1c1c10ec",
|
||||
"url": "https://api.github.com/repos/symfony/cache/zipball/d33cd9e14326e14a4145c21e600602eaf17cc9e7",
|
||||
"reference": "d33cd9e14326e14a4145c21e600602eaf17cc9e7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -3663,7 +3738,7 @@
|
|||
"psr6"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/cache/tree/v7.2.3"
|
||||
"source": "https://github.com/symfony/cache/tree/v7.2.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -3679,7 +3754,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-27T11:08:17+00:00"
|
||||
"time": "2025-02-26T09:57:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/cache-contracts",
|
||||
|
@ -3834,16 +3909,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/dependency-injection",
|
||||
"version": "v7.2.3",
|
||||
"version": "v7.2.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/dependency-injection.git",
|
||||
"reference": "1d321c4bc3fe926fd4c38999a4c9af4f5d61ddfc"
|
||||
"reference": "f0a1614cccb4b8431a97076f9debc08ddca321ca"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/1d321c4bc3fe926fd4c38999a4c9af4f5d61ddfc",
|
||||
"reference": "1d321c4bc3fe926fd4c38999a4c9af4f5d61ddfc",
|
||||
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f0a1614cccb4b8431a97076f9debc08ddca321ca",
|
||||
"reference": "f0a1614cccb4b8431a97076f9debc08ddca321ca",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -3894,7 +3969,7 @@
|
|||
"description": "Allows you to standardize and centralize the way objects are constructed in your application",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/dependency-injection/tree/v7.2.3"
|
||||
"source": "https://github.com/symfony/dependency-injection/tree/v7.2.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -3910,7 +3985,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-17T10:56:55+00:00"
|
||||
"time": "2025-02-21T09:47:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
|
@ -3981,16 +4056,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/error-handler",
|
||||
"version": "v7.2.3",
|
||||
"version": "v7.2.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/error-handler.git",
|
||||
"reference": "959a74d044a6db21f4caa6d695648dcb5584cb49"
|
||||
"reference": "aabf79938aa795350c07ce6464dd1985607d95d5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/error-handler/zipball/959a74d044a6db21f4caa6d695648dcb5584cb49",
|
||||
"reference": "959a74d044a6db21f4caa6d695648dcb5584cb49",
|
||||
"url": "https://api.github.com/repos/symfony/error-handler/zipball/aabf79938aa795350c07ce6464dd1985607d95d5",
|
||||
"reference": "aabf79938aa795350c07ce6464dd1985607d95d5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -4036,7 +4111,7 @@
|
|||
"description": "Provides tools to manage errors and ease debugging PHP code",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/error-handler/tree/v7.2.3"
|
||||
"source": "https://github.com/symfony/error-handler/tree/v7.2.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -4052,7 +4127,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-07T09:39:55+00:00"
|
||||
"time": "2025-02-02T20:27:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
|
@ -4342,16 +4417,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/framework-bundle",
|
||||
"version": "v7.2.3",
|
||||
"version": "v7.2.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/framework-bundle.git",
|
||||
"reference": "d37a43dd0b2079605fcab3056dac71934f06dc0f"
|
||||
"reference": "6d6614378cd8128eed0a037ce6ac51a26c5aaed5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/framework-bundle/zipball/d37a43dd0b2079605fcab3056dac71934f06dc0f",
|
||||
"reference": "d37a43dd0b2079605fcab3056dac71934f06dc0f",
|
||||
"url": "https://api.github.com/repos/symfony/framework-bundle/zipball/6d6614378cd8128eed0a037ce6ac51a26c5aaed5",
|
||||
"reference": "6d6614378cd8128eed0a037ce6ac51a26c5aaed5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -4472,7 +4547,7 @@
|
|||
"description": "Provides a tight integration between Symfony components and the Symfony full-stack framework",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/framework-bundle/tree/v7.2.3"
|
||||
"source": "https://github.com/symfony/framework-bundle/tree/v7.2.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -4488,7 +4563,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-29T07:13:55+00:00"
|
||||
"time": "2025-02-26T08:19:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
|
@ -4570,16 +4645,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v7.2.3",
|
||||
"version": "v7.2.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "caae9807f8e25a9b43ce8cc6fafab6cf91f0cc9b"
|
||||
"reference": "9f1103734c5789798fefb90e91de4586039003ed"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/caae9807f8e25a9b43ce8cc6fafab6cf91f0cc9b",
|
||||
"reference": "caae9807f8e25a9b43ce8cc6fafab6cf91f0cc9b",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/9f1103734c5789798fefb90e91de4586039003ed",
|
||||
"reference": "9f1103734c5789798fefb90e91de4586039003ed",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -4664,7 +4739,7 @@
|
|||
"description": "Provides a structured process for converting a Request into a Response",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v7.2.3"
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v7.2.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -4680,7 +4755,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-29T07:40:13+00:00"
|
||||
"time": "2025-02-26T11:01:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/intl",
|
||||
|
@ -4927,82 +5002,6 @@
|
|||
],
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php81",
|
||||
"version": "v1.31.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php81.git",
|
||||
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
|
||||
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php81\\": ""
|
||||
},
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php83",
|
||||
"version": "v1.31.0",
|
||||
|
@ -5323,16 +5322,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/twig-bridge",
|
||||
"version": "v7.2.2",
|
||||
"version": "v7.2.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/twig-bridge.git",
|
||||
"reference": "29e4c66de9618e67dc1f5f13bc667aca2a228f1e"
|
||||
"reference": "45c00afd4c9accf00a91215067c2858e5a9a3c4e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/29e4c66de9618e67dc1f5f13bc667aca2a228f1e",
|
||||
"reference": "29e4c66de9618e67dc1f5f13bc667aca2a228f1e",
|
||||
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/45c00afd4c9accf00a91215067c2858e5a9a3c4e",
|
||||
"reference": "45c00afd4c9accf00a91215067c2858e5a9a3c4e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -5413,7 +5412,7 @@
|
|||
"description": "Provides integration for Twig with various Symfony components",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/twig-bridge/tree/v7.2.2"
|
||||
"source": "https://github.com/symfony/twig-bridge/tree/v7.2.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -5429,7 +5428,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-19T14:25:03+00:00"
|
||||
"time": "2025-02-14T14:27:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/twig-bundle",
|
||||
|
@ -5600,16 +5599,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/var-exporter",
|
||||
"version": "v7.2.0",
|
||||
"version": "v7.2.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-exporter.git",
|
||||
"reference": "1a6a89f95a46af0f142874c9d650a6358d13070d"
|
||||
"reference": "4ede73aa7a73d81506002d2caadbbdad1ef5b69a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/1a6a89f95a46af0f142874c9d650a6358d13070d",
|
||||
"reference": "1a6a89f95a46af0f142874c9d650a6358d13070d",
|
||||
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/4ede73aa7a73d81506002d2caadbbdad1ef5b69a",
|
||||
"reference": "4ede73aa7a73d81506002d2caadbbdad1ef5b69a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -5656,7 +5655,7 @@
|
|||
"serialize"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/var-exporter/tree/v7.2.0"
|
||||
"source": "https://github.com/symfony/var-exporter/tree/v7.2.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -5672,7 +5671,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-18T07:58:17+00:00"
|
||||
"time": "2025-02-13T10:27:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "thecodingmachine/safe",
|
||||
|
@ -6006,20 +6005,20 @@
|
|||
},
|
||||
{
|
||||
"name": "twig/extra-bundle",
|
||||
"version": "v3.19.0",
|
||||
"version": "v3.20.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/twig-extra-bundle.git",
|
||||
"reference": "9746573ca4bc1cd03a767a183faadaf84e0c31fa"
|
||||
"reference": "9df5e1dbb6a68c0665ae5603f6f2c20815647876"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/9746573ca4bc1cd03a767a183faadaf84e0c31fa",
|
||||
"reference": "9746573ca4bc1cd03a767a183faadaf84e0c31fa",
|
||||
"url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/9df5e1dbb6a68c0665ae5603f6f2c20815647876",
|
||||
"reference": "9df5e1dbb6a68c0665ae5603f6f2c20815647876",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0.2",
|
||||
"php": ">=8.1.0",
|
||||
"symfony/framework-bundle": "^5.4|^6.4|^7.0",
|
||||
"symfony/twig-bundle": "^5.4|^6.4|^7.0",
|
||||
"twig/twig": "^3.2|^4.0"
|
||||
|
@ -6064,7 +6063,7 @@
|
|||
"twig"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.19.0"
|
||||
"source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.20.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -6076,24 +6075,24 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-26T19:22:23+00:00"
|
||||
"time": "2025-02-08T09:47:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twig/intl-extra",
|
||||
"version": "v3.19.0",
|
||||
"version": "v3.20.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/intl-extra.git",
|
||||
"reference": "79a1bea7254783b540d51de10dc5e9f310110794"
|
||||
"reference": "05bc5d46b9df9e62399eae53e7c0b0633298b146"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/intl-extra/zipball/79a1bea7254783b540d51de10dc5e9f310110794",
|
||||
"reference": "79a1bea7254783b540d51de10dc5e9f310110794",
|
||||
"url": "https://api.github.com/repos/twigphp/intl-extra/zipball/05bc5d46b9df9e62399eae53e7c0b0633298b146",
|
||||
"reference": "05bc5d46b9df9e62399eae53e7c0b0633298b146",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0.2",
|
||||
"php": ">=8.1.0",
|
||||
"symfony/intl": "^5.4|^6.4|^7.0",
|
||||
"twig/twig": "^3.13|^4.0"
|
||||
},
|
||||
|
@ -6128,7 +6127,7 @@
|
|||
"twig"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/twigphp/intl-extra/tree/v3.19.0"
|
||||
"source": "https://github.com/twigphp/intl-extra/tree/v3.20.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -6140,28 +6139,27 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-24T20:20:33+00:00"
|
||||
"time": "2025-01-31T20:45:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v3.19.0",
|
||||
"version": "v3.20.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "d4f8c2b86374f08efc859323dbcd95c590f7124e"
|
||||
"reference": "3468920399451a384bef53cf7996965f7cd40183"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/d4f8c2b86374f08efc859323dbcd95c590f7124e",
|
||||
"reference": "d4f8c2b86374f08efc859323dbcd95c590f7124e",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/3468920399451a384bef53cf7996965f7cd40183",
|
||||
"reference": "3468920399451a384bef53cf7996965f7cd40183",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0.2",
|
||||
"php": ">=8.1.0",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/polyfill-ctype": "^1.8",
|
||||
"symfony/polyfill-mbstring": "^1.3",
|
||||
"symfony/polyfill-php81": "^1.29"
|
||||
"symfony/polyfill-mbstring": "^1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.0",
|
||||
|
@ -6208,7 +6206,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twigphp/Twig/issues",
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.19.0"
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.20.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -6220,7 +6218,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-29T07:06:14+00:00"
|
||||
"time": "2025-02-13T08:34:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "wyrihaximus/composer-update-bin-autoload-path",
|
||||
|
@ -6808,7 +6806,8 @@
|
|||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": "^8.4",
|
||||
"ext-gd": "^8.4"
|
||||
"ext-gd": "^8.4",
|
||||
"ext-intl": "^8.4"
|
||||
},
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.6.0"
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"urn": "/account",
|
||||
"name": {
|
||||
"en": "Account",
|
||||
"ru": "Аккаунт"
|
||||
},
|
||||
"identifier": "account",
|
||||
"style": {
|
||||
"order": 1
|
||||
},
|
||||
"class": "",
|
||||
"icon": {
|
||||
"style": {},
|
||||
"class": "loading spinner animated"
|
||||
},
|
||||
"image": {
|
||||
"storage": null
|
||||
}
|
||||
}
|
|
@ -5,9 +5,9 @@
|
|||
"ru": "Корзина"
|
||||
},
|
||||
"style": {
|
||||
"order": 1
|
||||
"order": 999
|
||||
},
|
||||
"class": {},
|
||||
"class": "cart",
|
||||
"icon": {
|
||||
"style": {},
|
||||
"class": "shopping cart"
|
||||
|
|
|
@ -7,12 +7,10 @@
|
|||
"style": {
|
||||
"order": 0
|
||||
},
|
||||
"class": {},
|
||||
"class": "",
|
||||
"icon": {
|
||||
"style": {
|
||||
"rotate": "-135deg"
|
||||
},
|
||||
"class": "arrow circle"
|
||||
"style": {},
|
||||
"class": "house"
|
||||
},
|
||||
"image": {
|
||||
"storage": null
|
||||
|
|
|
@ -12,5 +12,16 @@
|
|||
"offer": false,
|
||||
"sim": null,
|
||||
"mail": null
|
||||
},
|
||||
"search": {
|
||||
"enabled": true,
|
||||
"position": "fixed"
|
||||
},
|
||||
"cart": {
|
||||
"enabled": true
|
||||
},
|
||||
"css": {
|
||||
"catalog-button-cart-background": "#40a7e3",
|
||||
"product-button-cart-background": "#40a7e3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +1,41 @@
|
|||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl ipv6only=on;
|
||||
#
|
||||
# This section is commented out to make it possible to run NGINX without errors
|
||||
# to generate TLS/SSL certificate via CertBot (see README.md)
|
||||
#
|
||||
# server {
|
||||
# listen 443 default_server ssl;
|
||||
# listen [::]:443 ssl default_server;
|
||||
|
||||
server_name domain.zone;
|
||||
# server_name domain.zone;
|
||||
|
||||
root /var/www/huesos/mirzaev/huesos/system/public;
|
||||
# root /var/www/huesos/mirzaev/huesos/system/public;
|
||||
|
||||
index index.php;
|
||||
# index index.php;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/domain.zone/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/domain.zone/privkey.pem;
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||
# ssl_certificate /etc/letsencrypt/live/domain.zone/fullchain.pem;
|
||||
# ssl_certificate_key /etc/letsencrypt/live/domain.zone/privkey.pem;
|
||||
# include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||
# ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php;
|
||||
}
|
||||
# location / {
|
||||
# try_files $uri $uri/ /index.php?$query_string;
|
||||
# }
|
||||
|
||||
location /api/cdek {
|
||||
rewrite ^/api/cdek(.*)$ /$1 break;
|
||||
index cdek.php;
|
||||
}
|
||||
# location /api/cdek {
|
||||
# rewrite ^/api/cdek(.*)$ /$1 break;
|
||||
# index cdek.php;
|
||||
# }
|
||||
|
||||
location ~ /(?<type>categories|products) {
|
||||
root /var/www/arming_bot/mirzaev/arming_bot/system/storage;
|
||||
try_files $uri =404;
|
||||
}
|
||||
# location ~ /(?<type>categories|products) {
|
||||
# root /var/www/huesos/mirzaev/huesos/system/storage;
|
||||
# try_files $uri =404;
|
||||
# }
|
||||
|
||||
location ~ \.php$ {
|
||||
include snippets/fastcgi-php.conf;
|
||||
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
|
||||
}
|
||||
}
|
||||
# location ~ \.php$ {
|
||||
# include snippets/fastcgi-php.conf;
|
||||
# fastcgi_pass unix:/run/php/php8.4-fpm.sock;
|
||||
# }
|
||||
# }
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
|
|
|
@ -0,0 +1,375 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\huesos\controllers\api\acquirings;
|
||||
|
||||
// Files of the project
|
||||
use mirzaev\huesos\controllers\core,
|
||||
mirzaev\huesos\models\order,
|
||||
mirzaev\huesos\models\cart,
|
||||
mirzaev\huesos\models\product,
|
||||
mirzaev\huesos\models\telegram,
|
||||
mirzaev\huesos\models\acquirings\robokassa as model,
|
||||
mirzaev\huesos\models\enumerations\currency,
|
||||
mirzaev\huesos\models\enumerations\language;
|
||||
|
||||
// Framework for PHP
|
||||
use mirzaev\minimal\http\enumerations\content,
|
||||
mirzaev\minimal\http\enumerations\status;
|
||||
|
||||
// Framework for ArangoDB
|
||||
use mirzaev\arangodb\collection,
|
||||
mirzaev\arangodb\document;
|
||||
|
||||
// Framework for Telegram
|
||||
use Zanzara\Zanzara as zanzara,
|
||||
Zanzara\Context as context,
|
||||
Zanzara\Config as config,
|
||||
Zanzara\Telegram\Type\Message as message;
|
||||
|
||||
// Event manager for PHP
|
||||
use React\EventLoop\Loop as loop;
|
||||
|
||||
// Built-in libraries
|
||||
use DateTime as datetime,
|
||||
Exception as exception;
|
||||
|
||||
/**
|
||||
* Controller of robokassa
|
||||
*
|
||||
* @package mirzaev\huesos\controllers\api\acquirings
|
||||
*
|
||||
* @param array $errors Registry of errors
|
||||
*
|
||||
* @method null result()
|
||||
* @method null success()
|
||||
* @method null fail()
|
||||
*
|
||||
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
final class robokassa extends core
|
||||
{
|
||||
/**
|
||||
* Errors
|
||||
*
|
||||
* @var array $errors Registry of errors
|
||||
*/
|
||||
protected array $errors = [
|
||||
'session' => [],
|
||||
'account' => [],
|
||||
'order' => [],
|
||||
'robokassa' => []
|
||||
];
|
||||
|
||||
/**
|
||||
* Result
|
||||
*
|
||||
* @param null
|
||||
*/
|
||||
public function result(
|
||||
?string $OutSum = null,
|
||||
?string $InvId = null,
|
||||
?string $SignatureValue = null,
|
||||
?string $PaymentMethod = null,
|
||||
?string $IncSum = null,
|
||||
?string $IncCurrLabel = null,
|
||||
?string $IsTest = null,
|
||||
?string $EMail = null,
|
||||
?string $Fee = null,
|
||||
string ...$other
|
||||
): null {
|
||||
// Searching for the order
|
||||
$order = order::_read(
|
||||
filter: 'd._key == @_key && d.paid != true',
|
||||
sort: 'd.created DESC, d._key DESC',
|
||||
amount: 1,
|
||||
parameters: ['_key' => $InvId],
|
||||
errors: $this->errors['robokassa']
|
||||
);
|
||||
|
||||
if ($order instanceof order) {
|
||||
// Initialized the order
|
||||
|
||||
if (model::result(identifier: (int) $InvId, cost: $OutSum, hash: $SignatureValue, test: (bool) $IsTest, errors: $this->errors['robokassa'])) {
|
||||
// Verified the payment
|
||||
|
||||
// Initializing the cart
|
||||
$cart = $order->cart();
|
||||
|
||||
if ($cart instanceof cart) {
|
||||
// Initialized the cart
|
||||
|
||||
if ((float) ($cart->cost() + ($cart->buffer['delivery']['cost'] ?? 0)) === (float) $OutSum) {
|
||||
// Full payment received
|
||||
|
||||
// Writing that the order being been paid
|
||||
$order->paid = true;
|
||||
/* $order->term = null; */
|
||||
|
||||
if (document::update($order->__document(), errors: $this->errors['order'])) {
|
||||
// Writed into ArangoDB
|
||||
|
||||
// Initializing identifier of the order
|
||||
$identifier = $order->__document()->getKey();
|
||||
|
||||
// Initializing the customer account
|
||||
$customer = $order->account();
|
||||
|
||||
$config = new config();
|
||||
$config->setParseMode(config::PARSE_MODE_MARKDOWN);
|
||||
$config->useReactFileSystem(true);
|
||||
/* $config->setLoop(loop::get()); */
|
||||
|
||||
$robot = new zanzara(TELEGRAM_KEY, $config);
|
||||
|
||||
// Sending the message
|
||||
$robot->getTelegram()->sendMessage("✅ *Заказ \#$identifier оплачен*", ['chat_id' => $customer->identifier])
|
||||
->then(function ($message) use ($robot, $cart, $order, $identifier, $customer) {
|
||||
// Sended the message
|
||||
|
||||
// Initializing the chats for sending registry
|
||||
$chats = [];
|
||||
|
||||
foreach ($this->settings->chats as $chat) {
|
||||
// Iteration over chats
|
||||
|
||||
if ($chat['orders']) {
|
||||
// Authorized to receive orders
|
||||
|
||||
// Writing into the chats for sending registry
|
||||
$chats[] = $chat['id'];
|
||||
}
|
||||
}
|
||||
|
||||
if (count($chats) > 0) {
|
||||
// Initialized chats
|
||||
|
||||
// Declaring the formatted list of products for message
|
||||
$list = '';
|
||||
|
||||
// Declaring total cost of products
|
||||
$cost = $cart->cost($list);
|
||||
|
||||
// Escaping the formatted list of products for the message
|
||||
$list = telegram::unmarkdown($list);
|
||||
|
||||
// Initializing currency symbol
|
||||
$symbol = ($account->currency ?? currency::rub)->symbol();
|
||||
|
||||
// Declaring delivery texts
|
||||
$delivery_cost = $delivery_days = $delivery_address = '';
|
||||
|
||||
if (!empty($cart->buffer['delivery'])) {
|
||||
// Initialized delibery data
|
||||
|
||||
// Initializing delivery cost for message
|
||||
$delivery_cost = telegram::unmarkdown((string) $cart->buffer['delivery']['cost']);
|
||||
|
||||
// Initializing delivery days for message
|
||||
$delivery_days = telegram::unmarkdown((string) $cart->buffer['delivery']['days']);
|
||||
|
||||
// Initializing delivery address for message
|
||||
$delivery_address = telegram::unmarkdown($cart->buffer['delivery']['location']['name'] . ', ' . $cart->buffer['delivery']['street']);
|
||||
}
|
||||
|
||||
// Initializing receiver domain for the message
|
||||
$domain = telegram::unmarkdown($customer->domain);
|
||||
|
||||
// Initializing receiver SIM for the message
|
||||
$sim = telegram::unmarkdown((string) $customer->receiver['sim']);
|
||||
|
||||
// Initializing receiver name for the message
|
||||
$name = telegram::unmarkdown($customer->receiver['name']);
|
||||
|
||||
// Initializing receiver address for the message
|
||||
$address = telegram::unmarkdown($customer->receiver['address']);
|
||||
|
||||
// Initializing the message cost part
|
||||
$part_cost = "*Стоимость:* $cost$symbol";
|
||||
if (!empty($delivery_cost)) $part_cost .= " \+ $delivery_cost$symbol";
|
||||
if (!empty($delivery_days)) $part_cost .= " \($delivery_days дней\)";
|
||||
|
||||
// Initializing the message delivery part
|
||||
$part_delivery = '';
|
||||
if (!empty($delivery_address)) $part_delivery .= "*Адрес доставки:* $delivery_address";
|
||||
if (!empty($part_delivery)) $part_delivery = "\n$part_delivery";
|
||||
|
||||
// Initializing the message receiver part
|
||||
$part_receiver = "*Получатель:* @$domain";
|
||||
if (!empty($sim)) $part_receiver .= " $sim";
|
||||
if (!empty($name)) $part_receiver .= "\n*$name*";
|
||||
if (!empty($address)) $part_receiver .= "\n\n*Адрес:* $address";
|
||||
|
||||
// Initializing the message commentary part
|
||||
$part_commentary = '';
|
||||
if (!empty($order->commentary)) $part_commentary .= "\n\n*Комментарий:* " . $order->commentary;
|
||||
|
||||
// Sending messages
|
||||
$robot->getTelegram()->sendBulkMessage(
|
||||
$chats,
|
||||
<<<TXT
|
||||
📦 *Заказ* \#$identifier
|
||||
|
||||
$list
|
||||
$part_cost$part_delivery
|
||||
|
||||
$part_receiver$part_commentary
|
||||
TXT,
|
||||
[
|
||||
'reply_markup' => [
|
||||
'inline_keyboard' => [
|
||||
[
|
||||
['text' => '✉️ Чат с покупателем', 'url' => 'https://t.me/' . $customer->domain]
|
||||
]
|
||||
],
|
||||
'disable_notification' => true
|
||||
]
|
||||
]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Sending response
|
||||
$this->response
|
||||
->start()
|
||||
->clean()
|
||||
->sse()
|
||||
->write("OK$identifier")
|
||||
->validate($this->request)
|
||||
?->body()
|
||||
->end();
|
||||
} else throw new exception('Failed to update the order');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exit (success/fail)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Success
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function success(
|
||||
?string $OutSum = null,
|
||||
?string $InvId = null,
|
||||
?string $SignatureValue = null,
|
||||
?string $IsTest = null,
|
||||
?string $Culture = null,
|
||||
string ...$other
|
||||
): null {
|
||||
// Searching for the order
|
||||
$order = order::_read(
|
||||
filter: 'd._key == @_key',
|
||||
sort: 'd.created DESC, d._key DESC',
|
||||
amount: 1,
|
||||
parameters: ['_key' => $InvId],
|
||||
errors: $this->errors['robokassa']
|
||||
);
|
||||
|
||||
if ($order instanceof order) {
|
||||
// Initialized the order
|
||||
|
||||
if (model::success(identifier: (int) $InvId, cost: $OutSum, hash: $SignatureValue, test: (bool) $IsTest, errors: $this->errors['robokassa'])) {
|
||||
// Verified the payment
|
||||
|
||||
if ($IsTest == 1) {
|
||||
// Enabled the test mode
|
||||
|
||||
// Initializing a paragraph abount the test mode
|
||||
$this->view->test = $this->language === language::ru ? 'Тестовый режим' : 'Test mode';
|
||||
}
|
||||
|
||||
// Render page
|
||||
$page = $this->view->render('api/acquirings/robokassa/success.html', [
|
||||
'h2' => $this->language === language::ru ? 'Заказ #' . $order->getKey() . ' оплачен' : 'Order #' . $order->getKey() . ' paid',
|
||||
'identifier' => $order->getKey(),
|
||||
'closing' => [
|
||||
'title' => $this->language === language::ru ? 'Закрытие окна' : 'Window closing',
|
||||
'iterator' => 30
|
||||
]
|
||||
]);
|
||||
|
||||
// Sending response
|
||||
$this->response
|
||||
->start()
|
||||
->clean()
|
||||
->sse()
|
||||
->write($page)
|
||||
->validate($this->request)
|
||||
?->body()
|
||||
->end();
|
||||
|
||||
// Deinitializing rendered page
|
||||
unset($page);
|
||||
}
|
||||
}
|
||||
|
||||
// Exit (success/fail)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function fail(
|
||||
?string $OutSum = null,
|
||||
?string $InvId = null,
|
||||
?string $IsTest = null,
|
||||
?string $Culture = null,
|
||||
string ...$other
|
||||
): null {
|
||||
// Searching for the order
|
||||
$order = order::_read(
|
||||
filter: 'd._key == @_key',
|
||||
sort: 'd.created DESC, d._key DESC',
|
||||
amount: 1,
|
||||
parameters: ['_key' => $InvId],
|
||||
errors: $this->errors['robokassa']
|
||||
);
|
||||
|
||||
if ($order instanceof order) {
|
||||
// Initialized the order
|
||||
|
||||
if ($IsTest == 1) {
|
||||
// Enabled the test mode
|
||||
|
||||
// Initializing a paragraph abount the test mode
|
||||
$this->view->test = $this->language === language::ru ? 'Тестовый режим' : 'Test mode';
|
||||
}
|
||||
|
||||
// Render page
|
||||
$page = $this->view->render('api/acquirings/robokassa/fail.html', [
|
||||
'h2' => $this->language === language::ru ? 'Заказ #' . $order->getKey() . ' не оплачен' : 'Order #' . $order->getKey() . ' not paid',
|
||||
'identifier' => $order->getKey(),
|
||||
'closing' => [
|
||||
'title' => $this->language === language::ru ? 'Закрытие окна' : 'Window closing',
|
||||
'iterator' => 30
|
||||
]
|
||||
]);
|
||||
|
||||
// Sending response
|
||||
$this->response
|
||||
->start()
|
||||
->clean()
|
||||
->sse()
|
||||
->write($page)
|
||||
->validate($this->request)
|
||||
?->body()
|
||||
->end();
|
||||
|
||||
// Deinitializing rendered page
|
||||
unset($page);
|
||||
}
|
||||
|
||||
// Exit (success/fail)
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -9,12 +9,23 @@ use mirzaev\huesos\controllers\core,
|
|||
mirzaev\huesos\models\cart as model,
|
||||
mirzaev\huesos\models\product,
|
||||
mirzaev\huesos\models\menu,
|
||||
mirzaev\huesos\models\telegram,
|
||||
mirzaev\huesos\models\enumerations\currency,
|
||||
mirzaev\huesos\models\enumerations\language;
|
||||
|
||||
// Framework for PHP
|
||||
use mirzaev\minimal\http\enumerations\content,
|
||||
mirzaev\minimal\http\enumerations\status;
|
||||
|
||||
// Framework for Telegram
|
||||
use Zanzara\Zanzara as zanzara,
|
||||
Zanzara\Context as context,
|
||||
Zanzara\Config as config,
|
||||
Zanzara\Telegram\Type\Message as message;
|
||||
|
||||
// Event manager for PHP
|
||||
use React\EventLoop\Loop as loop;
|
||||
|
||||
/**
|
||||
* Controller of cart
|
||||
*
|
||||
|
@ -113,12 +124,26 @@ final class cart extends core
|
|||
$this->view->formatted = $formatted;
|
||||
}
|
||||
|
||||
// Initializing types of avaiabld deliveries
|
||||
$this->view->deliveries = [
|
||||
'cdek' => [
|
||||
'label' => 'CDEK'
|
||||
]
|
||||
];
|
||||
if (array_search('address', $this->settings->input['deliveries']['site'], true) !== false) {
|
||||
// The deliveries input is enabled for the site
|
||||
|
||||
// Declaring the deliveries list
|
||||
$deliveries = [];
|
||||
|
||||
foreach ($this->settings->deliveries as $key => $value) {
|
||||
// Iterating over deliveries
|
||||
|
||||
if ($value['enabled']) {
|
||||
// The delivery is enabled
|
||||
|
||||
// Writing into the deliveries list
|
||||
$deliveries[$key] = ['label' => $value['label']];
|
||||
}
|
||||
}
|
||||
|
||||
// Writing the deliveries list into the templater variables
|
||||
$this->view->deliveries = $deliveries;
|
||||
}
|
||||
|
||||
if (str_contains($this->request->headers['accept'], content::json->value)) {
|
||||
// Request for JSON response
|
||||
|
@ -444,25 +469,134 @@ final class cart extends core
|
|||
}
|
||||
|
||||
/**
|
||||
* Pay
|
||||
* Attach
|
||||
*
|
||||
* Pay for the cart
|
||||
* Attach the cart
|
||||
*
|
||||
* @return null
|
||||
*
|
||||
* @todo кажется я сделал хуйню
|
||||
*/
|
||||
public function pay(): null
|
||||
public function attach(?string $share = null): null
|
||||
{
|
||||
if (str_contains($this->request->headers['accept'], content::json->value)) {
|
||||
// Request for JSON response
|
||||
|
||||
// Initializing the cart
|
||||
$this->cart ??= $this->account?->cart() ?? $this->session?->cart();
|
||||
if ($this->account) {
|
||||
// Initialized the account
|
||||
|
||||
if ($this->cart instanceof model) {
|
||||
$config = new config();
|
||||
$config->setParseMode(config::PARSE_MODE_MARKDOWN);
|
||||
$config->useReactFileSystem(true);
|
||||
/* $config->setLoop(loop::get()); */
|
||||
|
||||
$robot = new zanzara(TELEGRAM_KEY, $config);
|
||||
|
||||
// Initializing cart
|
||||
$cart = model::_read(
|
||||
filter: 'd.share == @share',
|
||||
sort: 'd.updated DESC, d.created DESC, d._key DESC',
|
||||
amount: 1,
|
||||
page: 1,
|
||||
parameters: ['share' => $share]
|
||||
);
|
||||
|
||||
// Deinitializing unnecessary variables
|
||||
unset($share);
|
||||
|
||||
if ($cart instanceof model) {
|
||||
// Initialized the cart
|
||||
|
||||
if ($share = $this->cart->share ?? $this->cart->share()) {
|
||||
// The cart is available for sharing
|
||||
// Unsharing the cart
|
||||
$cart->unshare();
|
||||
|
||||
// Connecting the cart to the account
|
||||
$edge = $this->account->connect($cart);
|
||||
|
||||
if (!empty($edge)) {
|
||||
// Connected the cart to the account
|
||||
|
||||
// Initializing products in the cart
|
||||
$products = $cart->products(language: $this->account->language ?? language::ru, currency: $this->account->currency ?? currency::rub);
|
||||
|
||||
if (!empty($products)) {
|
||||
// Initialized products in the cart
|
||||
|
||||
// Declaring the formatted list of products for message
|
||||
$list = '';
|
||||
|
||||
// Declaring total cost of products
|
||||
$cost = $cart->cost($list);
|
||||
|
||||
// Escaping the formatted list of products for the message
|
||||
$list = telegram::unmarkdown($list);
|
||||
|
||||
// Deinitializing unnecessary variables
|
||||
unset($products, $product, $row);
|
||||
|
||||
// Initializing identifier of the cart
|
||||
$identifier = $cart->getKey();
|
||||
|
||||
// Initializing currency symbol
|
||||
$symbol = ($this->account->currency ?? currency::rub)->symbol();
|
||||
|
||||
// Declaring delivery texts
|
||||
$delivery_cost = $delivery_days = $delivery_address = '';
|
||||
|
||||
if (!empty($cart->buffer['delivery'])) {
|
||||
// Initialized delibery data
|
||||
|
||||
// Initializing delivery cost for message
|
||||
$delivery_cost = telegram::unmarkdown((string) $cart->buffer['delivery']['cost']);
|
||||
|
||||
// Initializing delivery days for message
|
||||
$delivery_days = telegram::unmarkdown((string) $cart->buffer['delivery']['days']);
|
||||
|
||||
// Initializing delivery address for message
|
||||
$delivery_address = telegram::unmarkdown($cart->buffer['delivery']['location']['name'] . ', ' . $cart->buffer['delivery']['street']);
|
||||
}
|
||||
|
||||
// Initializing the message cost part
|
||||
$part_cost = "*Стоимость:* $cost$symbol";
|
||||
if (!empty($delivery_cost)) $part_cost .= " \+ $delivery_cost$symbol";
|
||||
if (!empty($delivery_days)) $part_cost .= " \($delivery_days дней\)";
|
||||
|
||||
// Initializing the message delivery part
|
||||
$part_delivery = '';
|
||||
if (!empty($delivery_address)) $part_cost .= "*Адрес доставки:* $delivery_address";
|
||||
if (!empty($part_delivery)) $part_delivery = "\n$part_delivery";
|
||||
|
||||
$robot->getTelegram()->sendMessage(
|
||||
<<<TXT
|
||||
🛒 *Добавлена корзина* \#$identifier
|
||||
|
||||
$list
|
||||
$part_cost$part_delivery
|
||||
TXT,
|
||||
[
|
||||
'chat_id' => $this->account->identifier,
|
||||
'reply_markup' => [
|
||||
'inline_keyboard' => [
|
||||
[
|
||||
['text' => '🚚 Оформить доставку', 'callback_data' => 'cart_delivery'],
|
||||
],
|
||||
],
|
||||
'disable_notification' => true
|
||||
]
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Deinitializing unnecessary variables
|
||||
unset($cart, $list);
|
||||
}
|
||||
}
|
||||
|
||||
// Deinitializing unnecessary variables
|
||||
unset($cart);
|
||||
|
||||
$robot->getLoop()->run();
|
||||
|
||||
// Sending response
|
||||
$this->response
|
||||
|
@ -470,7 +604,7 @@ final class cart extends core
|
|||
->clean()
|
||||
->sse()
|
||||
->json([
|
||||
'share' => $share,
|
||||
'success' => true,
|
||||
'errors' => $this->errors
|
||||
])
|
||||
->validate($this->request)
|
||||
|
@ -478,53 +612,6 @@ final class cart extends core
|
|||
->end();
|
||||
}
|
||||
|
||||
// Deinitializing unnecessary variables
|
||||
unset($hash);
|
||||
}
|
||||
}
|
||||
|
||||
// Exit (success/fail)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Robokassa
|
||||
*
|
||||
* HTML-document with robokassa iframe
|
||||
*
|
||||
* @param null
|
||||
*
|
||||
* @todo THIS MUST BE A PAYMENT OF ORDER IN THE FUTURE, NOT CART
|
||||
*/
|
||||
public function robokassa(): null
|
||||
{
|
||||
// Initializing the cart
|
||||
$this->cart ??= $this->account?->cart() ?? $this->session?->cart();
|
||||
|
||||
// Initializing the cart data
|
||||
$this->view->cart = $this->cart;
|
||||
$this->view->summary = $this->cart?->summary(currency: $this->currency);
|
||||
|
||||
if (str_contains($this->request->headers['accept'], content::any->value)) {
|
||||
// Request for any response
|
||||
|
||||
// Render page
|
||||
$page = $this->view->render('iframes/robokassa.html');
|
||||
|
||||
// Sending response
|
||||
$this->response
|
||||
->start()
|
||||
->clean()
|
||||
->sse()
|
||||
->write($page)
|
||||
->validate($this->request)
|
||||
?->body()
|
||||
->end();
|
||||
|
||||
// Deinitializing rendered page
|
||||
unset($page);
|
||||
}
|
||||
|
||||
// Exit (success/fail)
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace mirzaev\huesos\controllers;
|
|||
// Files of the project
|
||||
use mirzaev\huesos\controllers\core,
|
||||
mirzaev\huesos\models\enumerations\language,
|
||||
mirzaev\huesos\models\catalog as model,
|
||||
mirzaev\huesos\models\entry,
|
||||
mirzaev\huesos\models\category,
|
||||
mirzaev\huesos\models\product,
|
||||
|
@ -52,7 +53,8 @@ final class catalog extends core
|
|||
'session' => [],
|
||||
'account' => [],
|
||||
'menu' => [],
|
||||
'catalog' => []
|
||||
'catalog' => [],
|
||||
'cart' => []
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -80,9 +82,13 @@ final class catalog extends core
|
|||
// Initializing the cart
|
||||
$this->cart ??= $this->account?->cart() ?? $this->session?->cart();
|
||||
|
||||
// Initializing summary data of the cart
|
||||
$summary = $this->cart?->summary(currency: $this->currency, errors: $this->errors['cart']);
|
||||
|
||||
// Initializing the cart data
|
||||
$this->view->cart = [
|
||||
'products' => $this->cart?->products(language: $this->language, currency: $this->currency)
|
||||
'products' => $this->cart?->products(language: $this->language, currency: $this->currency),
|
||||
'summary' => $summary
|
||||
];
|
||||
|
||||
// Validating received product identifier
|
||||
|
@ -113,6 +119,7 @@ final class catalog extends core
|
|||
'cart' => [
|
||||
'amount' => $this->view->cart['products'][$this->view->product->getId()]['amount'] ?? 0,
|
||||
'text' => [
|
||||
'cart' => $this->language === language::ru ? 'Корзина' : 'Cart',
|
||||
'add' => $this->language === language::ru ? 'Добавить в корзину' : 'Add to the cart',
|
||||
'added' => $this->language === language::ru ? 'Добавлено в корзину' : 'Added to the cart'
|
||||
]
|
||||
|
@ -189,31 +196,26 @@ final class catalog extends core
|
|||
// Write to the response buffer
|
||||
$response['category'] = ['name' => $category->name ?? null];
|
||||
|
||||
// Search for categories that are descendants of $category
|
||||
$entries = entry::search(
|
||||
// Searching for entries that are descendants of $category
|
||||
$search = model::search(
|
||||
document: $category,
|
||||
amount: 50,
|
||||
amount: 200,
|
||||
depth: 100,
|
||||
categories_merge: 'name: v.name.@language',
|
||||
/* products_merge: 'DISTINCT MERGE(d, {name: d.name.@language, description: d.description.@language, compatibility: d.compatibility.@language, brand: d.brand.@language, cost: d.cost.@currency})', */
|
||||
parameters: ['language' => $this->language->name],
|
||||
errors: $this->errors['catalog']
|
||||
);
|
||||
|
||||
// Initialize buffers of entries (in singular, by parameter from ArangoDB)
|
||||
$category = $product = [];
|
||||
if (isset($this->settings->catalog['categories']['structure']) && $this->settings->catalog['categories']['structure'] === 'pages') {
|
||||
// Pages
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
// Iterate over entries (descendands)
|
||||
|
||||
// Write entry to the buffer of entries (sort by $category and $product)
|
||||
${$entry->_type}[] = $entry;
|
||||
// Write to the buffer of global variables of view templater
|
||||
$this->view->categories = $search['categories'];
|
||||
}
|
||||
|
||||
// Write to the buffer of global variables of view templater
|
||||
$this->view->categories = $category;
|
||||
|
||||
// Write to the buffer of global variables of view templater
|
||||
$this->view->products = $product;
|
||||
$this->view->products = $search['products'];
|
||||
|
||||
if (isset($this->view->products) && count($this->view->products) > 0) {
|
||||
// Amount of rendered products is more than 0
|
||||
|
@ -228,13 +230,51 @@ final class catalog extends core
|
|||
} else if (!isset($category)) {
|
||||
// Not received identifier of the category
|
||||
|
||||
// search for root ascendants categories
|
||||
if (isset($this->settings->catalog['categories']['structure']) && $this->settings->catalog['categories']['structure'] === 'pages') {
|
||||
// Pages
|
||||
|
||||
// Searching for root ascendants categories
|
||||
$this->view->categories = entry::ascendants(
|
||||
descendant: new category,
|
||||
return: 'DISTINCT MERGE(ascendant, { name: ascendant.name.@language})',
|
||||
return: 'DISTINCT MERGE(d, { name: d.name.@language})',
|
||||
parameters: ['language' => $this->language->name],
|
||||
errors: $this->errors['catalog']
|
||||
) ?? null;
|
||||
} else if (isset($this->settings->catalog['categories']['structure']) && $this->settings->catalog['categories']['structure'] === 'lists') {
|
||||
// Lists
|
||||
|
||||
// Initializing the list of all categories
|
||||
$categories = [];
|
||||
|
||||
// Initializing the structure of the list of all categories
|
||||
$structure = [];
|
||||
|
||||
foreach (
|
||||
entry::ascendants(
|
||||
descendant: new category,
|
||||
return: 'DISTINCT MERGE(d, { name: d.name.@language})',
|
||||
parameters: ['language' => $this->language->name],
|
||||
errors: $this->errors['catalog']
|
||||
) ?? null as $document
|
||||
) {
|
||||
// Iterating over found root ascendants categories
|
||||
|
||||
// Generating the list (entering into recursion)
|
||||
static::list(
|
||||
document: $document,
|
||||
language: $this->language,
|
||||
categories: $categories,
|
||||
structure: $structure,
|
||||
errors: $this->errors['catalog']
|
||||
);
|
||||
}
|
||||
|
||||
// Writing the list of all categories into the templater variable
|
||||
$this->view->categories = $categories;
|
||||
|
||||
// Writing the structure of the list of all categories into the templater variable
|
||||
$this->view->structure = $structure;
|
||||
}
|
||||
}
|
||||
|
||||
// Validating @todo add throwing errors
|
||||
|
@ -298,7 +338,7 @@ final class catalog extends core
|
|||
if ($this->view->menu instanceof _document) $this->view->menu = [$this->view->menu];
|
||||
}
|
||||
|
||||
if (str_contains($this->request->headers['accept'], content::json->value)) {
|
||||
if (str_contains($this->request->headers['accept'] ?? [], content::json->value)) {
|
||||
// Request for JSON response
|
||||
|
||||
// Initializing the response body buffer
|
||||
|
@ -387,4 +427,56 @@ final class catalog extends core
|
|||
// Exit (success/fail)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* List
|
||||
*
|
||||
*
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function list(_document $document, language $language, array &$categories = [], array &$structure = [], array &$errors = []): void
|
||||
{
|
||||
// Initializing the object
|
||||
$category = new category;
|
||||
|
||||
if (method_exists($category, '__document')) {
|
||||
// Object can implement a document from ArangoDB
|
||||
|
||||
// Writing the instance of product document from ArangoDB to the implement object
|
||||
$category->__document($document);
|
||||
|
||||
// Writing the category into the list of categories
|
||||
$categories[$category->getId()] = $category;
|
||||
|
||||
// Writing the category into the structure of the list categories
|
||||
$structure[$category->getId()] = [];
|
||||
|
||||
// Searching for entries that are descendants of $category
|
||||
$search = model::search(
|
||||
document: $category,
|
||||
amount: 200,
|
||||
categories_merge: 'name: v.name.@language',
|
||||
parameters: ['language' => $language->name],
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
if (count($search['categories']) > 0) {
|
||||
// Found descendants categories
|
||||
|
||||
foreach ($search['categories'] as $document) {
|
||||
// Iterating over descendants categories
|
||||
|
||||
// Entering into descendant level of the recursion for the list generation
|
||||
static::list(
|
||||
document: $document,
|
||||
language: $language,
|
||||
categories: $categories,
|
||||
structure: $structure[$category->getId()],
|
||||
errors: $errors
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,9 @@ final class delivery extends core
|
|||
'ready' => false
|
||||
];
|
||||
|
||||
if (array_search('address', $this->settings->input['deliveries']['site'], true) !== false) {
|
||||
// The deliveries address input is enabled for the site
|
||||
|
||||
if (isset($company)) {
|
||||
// Received company name
|
||||
|
||||
|
@ -102,6 +105,9 @@ final class delivery extends core
|
|||
// Deinitializing unnecessary variables
|
||||
unset($matches);
|
||||
|
||||
if ($this->settings->deliveries[$normalized]['enabled']) {
|
||||
// The delivery found and enabled
|
||||
|
||||
// Writing to the session buffer
|
||||
$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: ['delivery_company' => $normalized]));
|
||||
|
||||
|
@ -202,6 +208,7 @@ final class delivery extends core
|
|||
// Writing status of execution to the buffer of the response
|
||||
$response['status'] = 'success';
|
||||
}
|
||||
}
|
||||
} else if (isset($location)) {
|
||||
// Received location name
|
||||
|
||||
|
@ -1026,6 +1033,12 @@ final class delivery extends core
|
|||
unset($delivery, $location, $result, $normalized);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The deliveries input is not enabled for the site
|
||||
|
||||
// Reinitializing the ready status
|
||||
$response['ready'] = true;
|
||||
}
|
||||
|
||||
// Sending response
|
||||
$this->response
|
||||
|
@ -1057,7 +1070,7 @@ final class delivery extends core
|
|||
*/
|
||||
public function calculate(): null
|
||||
{
|
||||
if (str_contains($this->request->headers['accept'], content::json->value)) {
|
||||
if (str_contains($this->request->headers['accept'] ?? '', content::json->value)) {
|
||||
// Request for JSON response
|
||||
|
||||
// Initialization buffer of delivery parameters
|
||||
|
|
|
@ -53,6 +53,7 @@ final class session extends core
|
|||
* @param ?string $auth_date
|
||||
* @param ?string $hash
|
||||
* @param ?string $query_id
|
||||
* @param ?string $signature
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
|
@ -62,7 +63,8 @@ final class session extends core
|
|||
?string $chat_type = null,
|
||||
?string $auth_date = null,
|
||||
?string $hash = null,
|
||||
?string $query_id = null
|
||||
?string $query_id = null,
|
||||
?string $signature = null
|
||||
): null {
|
||||
if (str_contains($this->request->headers['accept'], content::json->value)) {
|
||||
// Request for JSON response
|
||||
|
@ -70,6 +72,12 @@ final class session extends core
|
|||
// Declaring variables in the correct scope
|
||||
$identifier = $domain = $language = null;
|
||||
|
||||
// Initializing data of the account
|
||||
$data = json_decode($user);
|
||||
|
||||
// Initializing avatar of the account
|
||||
$avatar = $data->photo_url;
|
||||
|
||||
if ($connected = isset($this->account)) {
|
||||
// Found the account
|
||||
|
||||
|
@ -84,10 +92,17 @@ final class session extends core
|
|||
} else {
|
||||
// Not found the account
|
||||
|
||||
if (isset($user, $chat_instance, $chat_type, $auth_date, $hash)) {
|
||||
if (isset($user, $auth_date, $hash)) {
|
||||
// Received required parameters
|
||||
|
||||
$buffer = ['user' => $user, 'chat_instance' => $chat_instance, 'chat_type' => $chat_type, 'auth_date' => $auth_date];
|
||||
$buffer = ['user' => $user];
|
||||
|
||||
if (isset($query_id)) $buffer += ['query_id' => $query_id];
|
||||
if (isset($signature)) $buffer += ['signature' => $signature];
|
||||
if (isset($chat_instance)) $buffer += ['chat_instance' => $chat_instance];
|
||||
if (isset($chat_type)) $buffer += ['chat_type' => $chat_type];
|
||||
|
||||
$buffer += ['auth_date' => $auth_date];
|
||||
|
||||
ksort($buffer);
|
||||
|
||||
|
@ -109,9 +124,6 @@ final class session extends core
|
|||
if (time() - $auth_date < 86400) {
|
||||
// Authorization date less than 1 day ago
|
||||
|
||||
// Initializing data of the account
|
||||
$data = json_decode($user);
|
||||
|
||||
// Initializing of the account
|
||||
$account = account::initialize(
|
||||
$data->id,
|
||||
|
@ -146,6 +158,9 @@ final class session extends core
|
|||
|
||||
// Initializing domain of the account
|
||||
$domain = $account->domain;
|
||||
|
||||
// Initializing avatar of the account
|
||||
$avatar = $data->photo_url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,6 +176,7 @@ final class session extends core
|
|||
'connected' => (bool) $connected,
|
||||
'identifier' => $identifier ?? null,
|
||||
'domain' => $domain ?? null,
|
||||
'avatar' => $avatar ?? null,
|
||||
'language' => $language?->name ?? null,
|
||||
'errors' => $this->errors
|
||||
])
|
||||
|
|
|
@ -86,7 +86,7 @@ final class account extends core implements document_interface, collection_inter
|
|||
if (method_exists($account, '__document')) {
|
||||
// Object can implement a document from ArangoDB
|
||||
|
||||
// Abstractioning of parameters
|
||||
// Implementinf parameters
|
||||
if (isset($result->language)) $result->language = language::{$result->language};
|
||||
if (isset($result->currency)) $result->currency = currency::{$result->currency};
|
||||
|
||||
|
@ -121,7 +121,7 @@ final class account extends core implements document_interface, collection_inter
|
|||
'messages' => $registration->getCanReadAllGroupMessages()
|
||||
],
|
||||
'premium' => $registration->isPremium(),
|
||||
'language' => language::{$registration->getLanguageCode()}->name ?? 'en',
|
||||
'language' => language::{$registration->getLanguageCode() ?? 'en'}?->name ?? 'en',
|
||||
'queries' => [
|
||||
'inline' => $registration->getSupportsInlineQueries()
|
||||
]
|
||||
|
@ -147,7 +147,6 @@ final class account extends core implements document_interface, collection_inter
|
|||
} else throw new exception('Failed to find account');
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
|
||||
} catch (exception $e) {
|
||||
var_dump($errors);
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
|
|
|
@ -0,0 +1,260 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\huesos\models\acquirings;
|
||||
|
||||
// Files of the project
|
||||
use mirzaev\huesos\models\core,
|
||||
mirzaev\huesos\models\settings,
|
||||
mirzaev\huesos\models\cart,
|
||||
mirzaev\huesos\models\order,
|
||||
mirzaev\huesos\models\enumerations\currency;
|
||||
|
||||
// Framework for ArangoDB
|
||||
use mirzaev\arangodb\collection,
|
||||
mirzaev\arangodb\document;
|
||||
|
||||
// Library for ArangoDB
|
||||
use ArangoDBClient\Document as _document;
|
||||
|
||||
// Built-in libraries
|
||||
use exception,
|
||||
DomainException as exception_domain,
|
||||
RuntimeException as exception_runtime;
|
||||
|
||||
/**
|
||||
* Model of robokassa
|
||||
*
|
||||
* @package mirzaev\huesos\models\acquirings
|
||||
*
|
||||
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
final class robokassa extends core
|
||||
{
|
||||
/**
|
||||
* Link
|
||||
*
|
||||
* Generate the link to pay for the order
|
||||
*
|
||||
* @param order $order The order
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return string|null The link to pay for the order, if generated
|
||||
*/
|
||||
public static function link(order $order, array &$errors = []): ?string
|
||||
{
|
||||
try {
|
||||
// Initializing the settings
|
||||
$settings = settings::active();
|
||||
|
||||
if ($settings instanceof settings) {
|
||||
// Initialized the settings
|
||||
|
||||
if ($settings->acquirings['robokassa']['enabled']) {
|
||||
// Enabled the robokassa acquiring
|
||||
|
||||
// Declaring the robokassa settings
|
||||
$robokassa = null;
|
||||
|
||||
// Initializing the test mode register
|
||||
$test = 0;
|
||||
|
||||
if ($settings->acquirings['robokassa']['mode'] === 'work') {
|
||||
// Work mode
|
||||
|
||||
// Initializing the robokassa settings
|
||||
$robokassa = require(SETTINGS . DIRECTORY_SEPARATOR . 'acquirings' . DIRECTORY_SEPARATOR . 'robokassa' . DIRECTORY_SEPARATOR . 'work.php');
|
||||
} else if ($settings->acquirings['robokassa']['mode'] === 'test') {
|
||||
// Test mode
|
||||
|
||||
// Reinitializing the test mode register
|
||||
$test = 1;
|
||||
|
||||
// Initializing the robokassa settings
|
||||
$robokassa = require(SETTINGS . DIRECTORY_SEPARATOR . 'acquirings' . DIRECTORY_SEPARATOR . 'robokassa' . DIRECTORY_SEPARATOR . 'test.php');
|
||||
} else {
|
||||
// Failed to initialized the mode
|
||||
|
||||
// Exit (fail)
|
||||
throw new exception_domain('Failed to initialize the robokassa mode');
|
||||
}
|
||||
|
||||
// Initializing the cart
|
||||
$cart = $order->cart();
|
||||
|
||||
if ($cart instanceof cart) {
|
||||
// Initialized the cart
|
||||
|
||||
// Initializing robokassa parameters
|
||||
$login = $robokassa['login'];
|
||||
$password = $robokassa['passwords'][0];
|
||||
|
||||
// Initializing the order parameters
|
||||
$identifier = $order->getKey();
|
||||
|
||||
// Initializing the invoice parameters
|
||||
$description = "Оплата заказа";
|
||||
$cost = $cart->cost() + ($cart->buffer['delivery']['cost'] ?? 0);
|
||||
|
||||
// Generatings the MD5 hash
|
||||
$md5 = md5("$login:$cost:$identifier:$password");
|
||||
/*
|
||||
// Writing the MD5 hash into the order implementator
|
||||
$order->acquirings = ['robokassa' => ['hash' => $md5] + ($order->acquirings['robokassa'] ?? [])] + ($order->acquirings ?? []);
|
||||
|
||||
if (document::update($order->__document(), errors: $errors)) {
|
||||
// Writed to ArangoDB */
|
||||
|
||||
// Exit (success)
|
||||
return "https://auth.robokassa.ru/Merchant/Index.aspx?MerchantLogin=$login&OutSum=$cost&InvId=$identifier&Description=$description&SignatureValue=$md5&IsTest=$test";
|
||||
/* } else throw new exception('Failed to write the robokassa hash into the order'); */
|
||||
} else {
|
||||
// Not initialized the cart
|
||||
|
||||
// Exit (fail)
|
||||
throw new exception_runtime('Failed to initialize the cart');
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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 null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify (result)
|
||||
*
|
||||
* Generate hash by arguments and verify for result with the hash
|
||||
*
|
||||
* @param int $identifier Identifier of the order
|
||||
* @param string $cost Cost of the order (can be with two zeros, like 1000.00)
|
||||
* @param string $hash Hash of the order
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return string|null The link to pay for the order, if generated
|
||||
*/
|
||||
public static function result(int $identifier, string $cost, string $hash, bool $test = false, array &$errors = []): ?bool
|
||||
{
|
||||
try {
|
||||
// Initializing the settings
|
||||
$settings = settings::active();
|
||||
|
||||
if ($settings instanceof settings) {
|
||||
// Initialized the settings
|
||||
|
||||
if ($settings->acquirings['robokassa']['enabled']) {
|
||||
// Enabled the robokassa acquiring
|
||||
|
||||
// Declaring the robokassa settings
|
||||
$robokassa = null;
|
||||
|
||||
if ($test) {
|
||||
// Test mode
|
||||
|
||||
// Initializing the robokassa settings
|
||||
$robokassa = require(SETTINGS . DIRECTORY_SEPARATOR . 'acquirings' . DIRECTORY_SEPARATOR . 'robokassa' . DIRECTORY_SEPARATOR . 'test.php');
|
||||
} else {
|
||||
// Work mode
|
||||
|
||||
// Initializing the robokassa settings
|
||||
$robokassa = require(SETTINGS . DIRECTORY_SEPARATOR . 'acquirings' . DIRECTORY_SEPARATOR . 'robokassa' . DIRECTORY_SEPARATOR . 'work.php');
|
||||
}
|
||||
|
||||
// Initializing robokassa parameters
|
||||
$password = $robokassa['passwords'][1];
|
||||
|
||||
// Generatings the MD5 hash
|
||||
$md5 = strtoupper(md5("$cost:$identifier:$password"));
|
||||
|
||||
// Exit (success)
|
||||
return $md5 === $hash;
|
||||
}
|
||||
}
|
||||
} 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 null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify (success)
|
||||
*
|
||||
* Generate hash by arguments and verify for success with the hash
|
||||
*
|
||||
* @param int $identifier Identifier of the order
|
||||
* @param string $cost Cost of the order (can be with two zeros, like 1000.00)
|
||||
* @param string $hash Hash of the order
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return string|null The link to pay for the order, if generated
|
||||
*/
|
||||
public static function success(int $identifier, string $cost, string $hash, bool $test = false, array &$errors = []): ?bool
|
||||
{
|
||||
try {
|
||||
// Initializing the settings
|
||||
$settings = settings::active();
|
||||
|
||||
if ($settings instanceof settings) {
|
||||
// Initialized the settings
|
||||
|
||||
if ($settings->acquirings['robokassa']['enabled']) {
|
||||
// Enabled the robokassa acquiring
|
||||
|
||||
// Declaring the robokassa settings
|
||||
$robokassa = null;
|
||||
|
||||
if ($test) {
|
||||
// Test mode
|
||||
|
||||
// Initializing the robokassa settings
|
||||
$robokassa = require(SETTINGS . DIRECTORY_SEPARATOR . 'acquirings' . DIRECTORY_SEPARATOR . 'robokassa' . DIRECTORY_SEPARATOR . 'test.php');
|
||||
} else {
|
||||
// Work mode
|
||||
|
||||
// Initializing the robokassa settings
|
||||
$robokassa = require(SETTINGS . DIRECTORY_SEPARATOR . 'acquirings' . DIRECTORY_SEPARATOR . 'robokassa' . DIRECTORY_SEPARATOR . 'work.php');
|
||||
}
|
||||
|
||||
// Initializing robokassa parameters
|
||||
$password = $robokassa['passwords'][0];
|
||||
|
||||
// Generatings the MD5 hash
|
||||
$md5 = md5("$cost:$identifier:$password");
|
||||
|
||||
// Exit (success)
|
||||
return $md5 === $hash;
|
||||
}
|
||||
}
|
||||
} 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 null;
|
||||
}
|
||||
}
|
|
@ -22,7 +22,8 @@ use mirzaev\arangodb\collection,
|
|||
use ArangoDBClient\Document as _document;
|
||||
|
||||
// Built-in libraries
|
||||
use exception;
|
||||
use DateTime as datetime,
|
||||
Exception as exception;
|
||||
|
||||
/**
|
||||
* Model of cart
|
||||
|
@ -184,7 +185,6 @@ final class cart extends core implements document_interface, collection_interfac
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Count
|
||||
*
|
||||
|
@ -242,7 +242,7 @@ final class cart extends core implements document_interface, collection_interfac
|
|||
/*
|
||||
* Write
|
||||
*
|
||||
* Write the product in the cart
|
||||
* Write the product into the cart
|
||||
*
|
||||
* @param product $product The product
|
||||
* @param int $amount Amount of writings
|
||||
|
@ -416,25 +416,262 @@ final class cart extends core implements document_interface, collection_interfac
|
|||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Order
|
||||
*
|
||||
/**
|
||||
* Account
|
||||
*
|
||||
* Search for the connected account
|
||||
*
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return void
|
||||
* @return account|null The connected account, if found
|
||||
*/
|
||||
public function order(array &$errors = []): void
|
||||
public function account(array &$errors = []): ?account
|
||||
{
|
||||
try {
|
||||
if ($result = collection::execute(
|
||||
<<<'AQL'
|
||||
FOR v IN 1..1 OUTBOUND @cart GRAPH users
|
||||
FILTER PARSE_IDENTIFIER(v._id).collection == @account
|
||||
LIMIT 1
|
||||
return v
|
||||
AQL,
|
||||
[
|
||||
'cart' => $this->document->getId(),
|
||||
'account' => account::COLLECTION
|
||||
],
|
||||
errors: $errors
|
||||
)) {
|
||||
// Found the instance of the account connected to the account
|
||||
|
||||
// Initializing the object
|
||||
$account = new account;
|
||||
|
||||
if (method_exists($account, '__document')) {
|
||||
// Object can implement a document from ArangoDB
|
||||
|
||||
// Implementinf parameters
|
||||
if (isset($result->language)) $result->language = language::{$result->language};
|
||||
if (isset($result->currency)) $result->currency = currency::{$result->currency};
|
||||
|
||||
// Writing the instance of the account document from ArangoDB to the implement object
|
||||
$account->__document($result);
|
||||
|
||||
// Exit (success)
|
||||
return $account;
|
||||
}
|
||||
}
|
||||
} 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 null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cost
|
||||
*
|
||||
* Generate the cart products cost
|
||||
*
|
||||
* @param string &$list List of the calculated products
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return float|int|null The cart products cost, if generated
|
||||
*/
|
||||
public function cost(string &$list = '', array &$errors = []): float|int|null
|
||||
{
|
||||
try {
|
||||
// Initializing the account
|
||||
$account = $this->account($errors);
|
||||
|
||||
if ($account instanceof account) {
|
||||
// Initialized the account
|
||||
|
||||
// Initializing products in the cart
|
||||
$products = $this->products(language: $account->language ?? language::ru, currency: $account->currency ?? currency::rub);
|
||||
|
||||
if (!empty($products)) {
|
||||
// Initialized products in the cart
|
||||
|
||||
// Declaring total cost of products
|
||||
$cost = 0;
|
||||
|
||||
// Initializing iterator of rows
|
||||
$row = 0;
|
||||
|
||||
foreach ($products as $product) {
|
||||
// Iterating over products
|
||||
|
||||
// Generating formatted list of products for message
|
||||
$list .= ++$row . '. ' . $product['document']['name'] . ' (' . $product['amount'] . 'шт)' . "\n";
|
||||
|
||||
// Generating total cost of products
|
||||
$cost += $product['document']['cost'] * $product['amount'];
|
||||
}
|
||||
|
||||
// Exit (success)
|
||||
return $cost;
|
||||
}
|
||||
}
|
||||
} 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 null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Order
|
||||
*
|
||||
* Create the order and connect to the cart and to the account
|
||||
* Make the cart ordered (the account will create a new cart)
|
||||
*
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return order|null The order, if created
|
||||
*
|
||||
* @todo Handling errors
|
||||
*/
|
||||
public function order(array &$errors = []): ?order
|
||||
{
|
||||
try {
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
if (collection::initialize(reservation::COLLECTION, reservation::TYPE, errors: $errors)) {
|
||||
if (collection::initialize(order::COLLECTION, order::TYPE, errors: $errors)) {
|
||||
if (collection::initialize(reservation::COLLECTION, reservation::TYPE, errors: $errors)) {
|
||||
if (collection::initialize(account::COLLECTION, account::TYPE, errors: $errors)) {
|
||||
// Initialized collections
|
||||
|
||||
} else throw new exception('Failed to initialize ' . product::TYPE->name . ' collection: ' . product::COLLECTION);
|
||||
// Searching for the order
|
||||
$existed = collection::execute(
|
||||
<<<'AQL'
|
||||
FOR v IN 1..1 OUTBOUND @cart GRAPH users
|
||||
FILTER PARSE_IDENTIFIER(v._id).collection == @order
|
||||
LIMIT 1
|
||||
return v
|
||||
AQL,
|
||||
[
|
||||
'cart' => $this->document->getId(),
|
||||
'order' => order::COLLECTION
|
||||
],
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
if ($existed) {
|
||||
// The order has already been created
|
||||
|
||||
// Initializing the object
|
||||
$order = new order;
|
||||
|
||||
if (method_exists($order, '__document')) {
|
||||
// Object can implement a document from ArangoDB
|
||||
|
||||
// Writing the instance of the order document from ArangoDB to the implement object
|
||||
$order->__document($existed);
|
||||
|
||||
// Exit (success)
|
||||
return $order;
|
||||
}
|
||||
} else {
|
||||
// The order has not been created
|
||||
|
||||
// Initializing a new order
|
||||
$_id = document::write(
|
||||
order::COLLECTION,
|
||||
[
|
||||
'active' => true,
|
||||
/* 'term' => (int) new datetime('+15 minutes')->ormat('U') */
|
||||
]
|
||||
);
|
||||
|
||||
if ($result = collection::execute(
|
||||
<<<'AQL'
|
||||
FOR d IN @@collection
|
||||
FILTER d._id == @_id && d.active == true
|
||||
RETURN d
|
||||
AQL,
|
||||
[
|
||||
'@collection' => order::COLLECTION,
|
||||
'_id' => $_id
|
||||
],
|
||||
errors: $errors
|
||||
)) {
|
||||
// Found the instance of just created the new order
|
||||
|
||||
// Writing the ordered status for the cart
|
||||
$this->document->ordered = true;
|
||||
|
||||
if (document::update($this->__document(), errors: $errors)) {
|
||||
// Writed into ArangoDB
|
||||
|
||||
// Initializing the object
|
||||
$order = new order;
|
||||
|
||||
if (method_exists($order, '__document')) {
|
||||
// Object can implement a document from ArangoDB
|
||||
|
||||
// Writing the instance of the order document from ArangoDB to the implement object
|
||||
$order->__document($result);
|
||||
|
||||
// Connecting the cart to the order
|
||||
$connected = $order->connect($this, $errors);
|
||||
|
||||
if ($connected) {
|
||||
// Connected the cart with the order
|
||||
|
||||
if ($result = collection::execute(
|
||||
<<<'AQL'
|
||||
FOR v IN 1..1 OUTBOUND @cart GRAPH users
|
||||
FILTER PARSE_IDENTIFIER(v._id).collection == @account
|
||||
LIMIT 1
|
||||
return v
|
||||
AQL,
|
||||
[
|
||||
'cart' => $this->document->getId(),
|
||||
'account' => account::COLLECTION
|
||||
],
|
||||
errors: $errors
|
||||
)) {
|
||||
// Found the instance of the account connected to the cart
|
||||
|
||||
// Initializing the object
|
||||
$account = new account;
|
||||
|
||||
if (method_exists($account, '__document')) {
|
||||
// Object can implement a document from ArangoDB
|
||||
|
||||
// Writing the instance of the account document from ArangoDB to the implement object
|
||||
$account->__document($result);
|
||||
|
||||
// Connecting the account with the order
|
||||
$connected = $account->connect($order, $errors);
|
||||
|
||||
if ($connected) {
|
||||
// Connected the account with the order
|
||||
|
||||
// Exit (success)
|
||||
return $order;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else throw new exception('Class ' . order::class . ' does not implement a document from ArangoDB');
|
||||
} else throw new exception('Failed to write the ordered status for the cart');
|
||||
} else throw new exception('Failed to create or find just created ' . static::class);
|
||||
}
|
||||
} else throw new exception('Failed to initialize ' . account::TYPE->name . ' collection: ' . account::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . reservation::TYPE->name . ' collection: ' . reservation::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . order::TYPE->name . ' collection: ' . order::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
|
||||
} catch (exception $e) {
|
||||
// Writing to the registry of errors
|
||||
|
@ -445,5 +682,8 @@ final class cart extends core implements document_interface, collection_interfac
|
|||
'stack' => $e->getTrace()
|
||||
];
|
||||
}
|
||||
|
||||
// Exit (fail)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,64 @@ use GdImage as image;
|
|||
final class catalog extends core
|
||||
{
|
||||
use yandex, files {
|
||||
yandex::download as yandex;
|
||||
yandex::download as file;
|
||||
yandex::list as folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search
|
||||
*
|
||||
* @param category|product $document Ascendant document
|
||||
* @param string|null $filter Expression for filtering (AQL)
|
||||
* @param string|null $sort Expression for sorting (AQL)
|
||||
* @param int $depth Amount of nodes for traversal search (subcategories/products inside subcategories...)
|
||||
* @param int $page Page
|
||||
* @param int $amount Amount of documents per page
|
||||
* @param string|null $categories_merge Expression with paremeters to return for categories (AQL)
|
||||
* @param string|null $products_merge Expression with paremeters to return for products (AQL)
|
||||
* @param array $parameters Binded parameters for placeholders ['placeholder' => parameter]
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return array [categories => [document::class...], $products => [document::class...]]
|
||||
*/
|
||||
public static function search(
|
||||
category|product $document,
|
||||
?string $filter = 'v.deleted != true && v.hidden != true',
|
||||
?string $sort = 'v.position ASC, v.created DESC',
|
||||
int $depth = 1,
|
||||
int $page = 1,
|
||||
int $amount = 100,
|
||||
?string $categories_merge = null,
|
||||
?string $products_merge = null,
|
||||
array $parameters = [],
|
||||
array &$errors = []
|
||||
): array {
|
||||
// Search for entries that are descendants of $category
|
||||
$entries = entry::search(
|
||||
document: $document,
|
||||
filter: $filter,
|
||||
sort: $sort,
|
||||
depth: $depth,
|
||||
page: $page,
|
||||
amount: $amount,
|
||||
categories_merge: $categories_merge,
|
||||
products_merge: $products_merge,
|
||||
parameters: $parameters,
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
// Initialize buffers of entries (in singular, by parameter from ArangoDB)
|
||||
$category = $product = [];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
// Iterate over entries (descendands)
|
||||
|
||||
// Write entry to the buffer of entries (sort by $category and $product)
|
||||
${$entry->_type}[] = $entry;
|
||||
}
|
||||
|
||||
// Exit (success)
|
||||
return ['categories' => $category, 'products' => $product];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,7 +177,7 @@ final class catalog extends core
|
|||
// Iterate over categories
|
||||
|
||||
try {
|
||||
if (!empty($row['identifier']) && !empty($row['name'])) {
|
||||
if (!empty($row['identifier']) && is_int($row['identifier']) && $row['identifier'] > 0 && !empty($row['name'])) {
|
||||
// Required cells are filled in
|
||||
|
||||
// Incrementing the counter of loaded categories
|
||||
|
@ -195,7 +252,10 @@ final class catalog extends core
|
|||
// Received images
|
||||
|
||||
// Initializing new images of the category
|
||||
$images = explode(' ', mb_trim($row['images']));
|
||||
$images = static::folder(
|
||||
uri: explode(' ', mb_trim($row['images']))[0],
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
// Reinitialize images? (true, if no images found or their amount does not match)
|
||||
/* $reinitialize = !$category->images || count($category->images) !== count($images); */
|
||||
|
@ -210,14 +270,14 @@ final class catalog extends core
|
|||
// Initializing the buffer of images
|
||||
$buffer = [];
|
||||
|
||||
foreach ($images as $index => $file) {
|
||||
foreach (is_array($images) ? $images : [] as $image) {
|
||||
// Iterating over new images
|
||||
|
||||
// Skipping empty URI`s
|
||||
if (empty($file = mb_trim($file))) continue;
|
||||
// Initializing identifier of the image
|
||||
$identifier = preg_replace('/\.\w+$/', '', $image->name);
|
||||
|
||||
// Initializing path to directory of images in storage
|
||||
$directory = DIRECTORY_SEPARATOR . 'categories' . DIRECTORY_SEPARATOR . $row['identifier'] . DIRECTORY_SEPARATOR . $index;
|
||||
$directory = DIRECTORY_SEPARATOR . 'categories' . DIRECTORY_SEPARATOR . $row['identifier'] . DIRECTORY_SEPARATOR . $identifier;
|
||||
|
||||
// Initializing URL of the image in storage
|
||||
$url = STORAGE . $directory;
|
||||
|
@ -225,8 +285,8 @@ final class catalog extends core
|
|||
// Initializing the directory in storage
|
||||
if (!file_exists($url)) mkdir($url, 0775, true);
|
||||
|
||||
if ($downloaded = static::yandex(
|
||||
uri: $file,
|
||||
if ($downloaded = static::file(
|
||||
uri: $image->public_url ?? $image->public_key,
|
||||
destination: $url,
|
||||
name: 'source',
|
||||
errors: $errors
|
||||
|
@ -249,6 +309,11 @@ final class catalog extends core
|
|||
|
||||
// Initializing implementator of the image
|
||||
$boba = imagecreatefromjpeg($uri);
|
||||
} else if ($downloaded['content'] === content::webp) {
|
||||
// WEBP
|
||||
|
||||
// Initializing implementator of the image
|
||||
$boba = imagecreatefromwebp($uri);
|
||||
}
|
||||
|
||||
// Enabling better antialiasing
|
||||
|
@ -280,10 +345,10 @@ final class catalog extends core
|
|||
imagecopyresampled($biba, $boba, 0, 0, 0, 0, $width, $height, $size[0], $size[1]);
|
||||
|
||||
// Initializing URI of the resized image
|
||||
$uri = $directory . DIRECTORY_SEPARATOR . "$resize." . $downloaded['content']->extension();
|
||||
$uri = $directory . DIRECTORY_SEPARATOR . "$resize.webp";
|
||||
|
||||
// Saving the image
|
||||
imagePng($biba, STORAGE . $uri);
|
||||
imagewebp($biba, STORAGE . $uri);
|
||||
|
||||
// Writing the resized image to the buffer of resized images
|
||||
$resized[$resize] = $uri;
|
||||
|
@ -291,7 +356,7 @@ final class catalog extends core
|
|||
|
||||
// Writing the image to the buffer if images
|
||||
$buffer[] = [
|
||||
'source' => $file,
|
||||
'source' => $image->public_url ?? null,
|
||||
'storage' => [
|
||||
'source' => $directory . DIRECTORY_SEPARATOR . $downloaded['name'] . '.' . $downloaded['content']->extension(),
|
||||
] + $resized
|
||||
|
@ -349,7 +414,7 @@ final class catalog extends core
|
|||
// Iterate over products
|
||||
|
||||
try {
|
||||
if (!empty($row['identifier']) && !empty($row['name'])) {
|
||||
if (!empty($row['identifier']) && is_int($row['identifier']) && $row['identifier'] > 0 && !empty($row['name'])) {
|
||||
// Required cells are filled in
|
||||
|
||||
// Incrementing the counter of loaded products
|
||||
|
@ -385,7 +450,7 @@ final class catalog extends core
|
|||
|
||||
// Initializing position of the product
|
||||
if (empty($product->position) || $product->position !== $row['position'])
|
||||
$product->position = $row['position'];
|
||||
$product->position = isset($row['position']) ? (int) $row['position'] : 0;
|
||||
} else {
|
||||
// Not initialized the product
|
||||
|
||||
|
@ -399,7 +464,7 @@ final class catalog extends core
|
|||
dimensions: ['x' => $row['x'], 'y' => $row['y'], 'z' => $row['z']],
|
||||
brand: [$language->name => $row['brand']],
|
||||
compatibility: [$language->name => $row['compatibility']],
|
||||
position: (int) $row['position'] ?? null,
|
||||
position: isset($row['position']) ? (int) $row['position'] : 0,
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
|
@ -436,8 +501,11 @@ final class catalog extends core
|
|||
if (!empty($row['images'])) {
|
||||
// Received images
|
||||
|
||||
// Initializing new images of the category
|
||||
$images = explode(' ', mb_trim($row['images']));
|
||||
// Initializing new images of the product
|
||||
$images = static::folder(
|
||||
uri: explode(' ', mb_trim($row['images']))[0],
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
// Reinitialize images? (true, if no images found or their amount does not match)
|
||||
/* $reinitialize = !$product->images || count($product->images) !== count($images); */
|
||||
|
@ -452,14 +520,14 @@ final class catalog extends core
|
|||
// Initializing the buffer of images
|
||||
$buffer = [];
|
||||
|
||||
foreach ($images as $index => $file) {
|
||||
foreach (is_array($images) ? $images : [] as $image) {
|
||||
// Iterating over new images
|
||||
|
||||
// Skipping empty URI`s
|
||||
if (empty($file = mb_trim($file))) continue;
|
||||
// Initializing identifier of the image
|
||||
$identifier = preg_replace('/\.\w+$/', '', $image->name);
|
||||
|
||||
// Initializing path to directory of images in storage
|
||||
$directory = DIRECTORY_SEPARATOR . 'products' . DIRECTORY_SEPARATOR . $row['identifier'] . DIRECTORY_SEPARATOR . $index;
|
||||
$directory = DIRECTORY_SEPARATOR . 'products' . DIRECTORY_SEPARATOR . $row['identifier'] . DIRECTORY_SEPARATOR . $identifier;
|
||||
|
||||
// Initializing URL of the image in storage
|
||||
$url = STORAGE . $directory;
|
||||
|
@ -467,8 +535,8 @@ final class catalog extends core
|
|||
// Initializing the directory in storage
|
||||
if (!file_exists($url)) mkdir($url, 0775, true);
|
||||
|
||||
if ($downloaded = static::yandex(
|
||||
uri: $file,
|
||||
if ($downloaded = static::file(
|
||||
uri: $image->public_url ?? $image->public_key,
|
||||
destination: $url,
|
||||
name: 'source',
|
||||
errors: $errors
|
||||
|
@ -491,6 +559,11 @@ final class catalog extends core
|
|||
|
||||
// Initializing implementator of the image
|
||||
$boba = imagecreatefromjpeg($uri);
|
||||
} else if ($downloaded['content'] === content::webp) {
|
||||
// WEBP
|
||||
|
||||
// Initializing implementator of the image
|
||||
$boba = imagecreatefromwebp($uri);
|
||||
}
|
||||
|
||||
// Enabling better antialiasing
|
||||
|
@ -522,10 +595,10 @@ final class catalog extends core
|
|||
imagecopyresampled($biba, $boba, 0, 0, 0, 0, $width, $height, $size[0], $size[1]);
|
||||
|
||||
// Initializing URI of the resized image
|
||||
$uri = $directory . DIRECTORY_SEPARATOR . "$resize." . $downloaded['content']->extension();
|
||||
$uri = $directory . DIRECTORY_SEPARATOR . "$resize.webp";
|
||||
|
||||
// Saving the image
|
||||
imagePng($biba, STORAGE . $uri);
|
||||
imagewebp($biba, STORAGE . $uri);
|
||||
|
||||
// Writing the resized image to the buffer of resized images
|
||||
$resized[$resize] = $uri;
|
||||
|
@ -533,7 +606,7 @@ final class catalog extends core
|
|||
|
||||
// Writing the image to the buffer if images
|
||||
$buffer[] = [
|
||||
'source' => $file,
|
||||
'source' => $image->public_url ?? null,
|
||||
'storage' => [
|
||||
'source' => $directory . DIRECTORY_SEPARATOR . $downloaded['name'] . '.' . $downloaded['content']->extension(),
|
||||
] + $resized
|
||||
|
|
|
@ -116,11 +116,16 @@ final class entry extends core implements document_interface, collection_interfa
|
|||
if ($result = collection::execute(
|
||||
sprintf(
|
||||
<<<'AQL'
|
||||
FOR d IN @@collection
|
||||
FOR ascendant IN OUTBOUND d @@edge
|
||||
let from = (
|
||||
FOR e IN @@edge
|
||||
RETURN DISTINCT e._from
|
||||
)
|
||||
|
||||
FOR d in @@collection
|
||||
FILTER !POSITION(from, d._id)
|
||||
RETURN %s
|
||||
AQL,
|
||||
empty($return) ? 'DISTINCT ascendant' : $return
|
||||
empty($return) ? 'DISTINCT d' : $return
|
||||
),
|
||||
[
|
||||
'@collection' => $descendant::COLLECTION,
|
||||
|
@ -218,8 +223,9 @@ final class entry extends core implements document_interface, collection_interfa
|
|||
* @param category|product $document Ascendant document
|
||||
* @param string|null $filter Expression for filtering (AQL)
|
||||
* @param string|null $sort Expression for sorting (AQL)
|
||||
* @param int $page Страница
|
||||
* @param int $amount Количество товаров на странице
|
||||
* @param int $depth Amount of nodes for traversal search (subcategories/products inside subcategories...)
|
||||
* @param int $page Page
|
||||
* @param int $amount Amount of documents per page
|
||||
* @param string|null $categories_merge Expression with paremeters to return for categories (AQL)
|
||||
* @param string|null $products_merge Expression with paremeters to return for products (AQL)
|
||||
* @param array $parameters Binded parameters for placeholders ['placeholder' => parameter]
|
||||
|
@ -231,6 +237,7 @@ final class entry extends core implements document_interface, collection_interfa
|
|||
category|product $document,
|
||||
?string $filter = 'v.deleted != true && v.hidden != true',
|
||||
?string $sort = 'v.position ASC, v.created DESC',
|
||||
int $depth = 1,
|
||||
int $page = 1,
|
||||
int $amount = 100,
|
||||
?string $categories_merge = null,
|
||||
|
@ -247,12 +254,13 @@ final class entry extends core implements document_interface, collection_interfa
|
|||
return is_array($result = collection::execute(
|
||||
sprintf(
|
||||
<<<'AQL'
|
||||
FOR v IN 1..1 INBOUND @document GRAPH @graph
|
||||
FOR v IN 1..%u INBOUND @document GRAPH @graph
|
||||
%s
|
||||
%s
|
||||
LIMIT @offset, @amount
|
||||
RETURN DISTINCT IS_SAME_COLLECTION(@category, v._id) ? MERGE(v, {_type: @category%s}) : MERGE(v, {_type: @product%s})
|
||||
AQL,
|
||||
$depth,
|
||||
empty($filter) ? '' : "FILTER $filter",
|
||||
empty($sort) ? '' : "SORT $sort",
|
||||
empty($categories_merge) ? '' : ", $categories_merge",
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\huesos\models\enumerations;
|
||||
|
||||
// Files of the project
|
||||
use mirzaev\huesos\models\enumerations\language;
|
||||
|
||||
/**
|
||||
* Types of destination
|
||||
*
|
||||
* @package mirzaev\huesos\models\enumerations
|
||||
*
|
||||
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
enum destination
|
||||
{
|
||||
case office;
|
||||
case door;
|
||||
|
||||
/**
|
||||
* Label
|
||||
*
|
||||
* Initialize label of the destination
|
||||
*
|
||||
* @param language|null $language Language into which to translate
|
||||
*
|
||||
* @return string Translated label of the destination
|
||||
*/
|
||||
public function label(?language $language = language::en): string
|
||||
{
|
||||
// Exit (success)
|
||||
return match ($this) {
|
||||
destination::office => match ($language) {
|
||||
language::en => 'Office',
|
||||
language::ru => 'Пункт выдачи'
|
||||
},
|
||||
destination::door => match ($language) {
|
||||
language::en => 'Door',
|
||||
language::ru => 'До двери'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@ namespace mirzaev\huesos\models;
|
|||
|
||||
// Files of the project
|
||||
use mirzaev\huesos\models\core,
|
||||
mirzaev\huesos\models\cart,
|
||||
mirzaev\huesos\models\account,
|
||||
mirzaev\huesos\models\reservation,
|
||||
mirzaev\huesos\models\traits\document as document_trait,
|
||||
mirzaev\huesos\models\interfaces\document as document_interface,
|
||||
|
@ -40,4 +42,115 @@ final class order extends core implements document_interface, collection_interfa
|
|||
* Name of the collection in ArangoDB
|
||||
*/
|
||||
final public const string COLLECTION = 'order';
|
||||
|
||||
/**
|
||||
* Cart
|
||||
*
|
||||
* Search for the connected cart
|
||||
*
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return cart|null The connected cart, if found
|
||||
*/
|
||||
public function cart(array &$errors = []): ?cart
|
||||
{
|
||||
try {
|
||||
if ($result = collection::execute(
|
||||
<<<'AQL'
|
||||
FOR v IN 1..1 INBOUND @order GRAPH users
|
||||
FILTER PARSE_IDENTIFIER(v._id).collection == @cart
|
||||
LIMIT 1
|
||||
return v
|
||||
AQL,
|
||||
[
|
||||
'order' => $this->document->getId(),
|
||||
'cart' => cart::COLLECTION
|
||||
],
|
||||
errors: $errors
|
||||
)) {
|
||||
// Found the instance of the cart connected to the order
|
||||
|
||||
// Initializing the object
|
||||
$cart = new cart;
|
||||
|
||||
if (method_exists($cart, '__document')) {
|
||||
// Object can implement a document from ArangoDB
|
||||
|
||||
// Writing the instance of the cart document from ArangoDB to the implement object
|
||||
$cart->__document($result);
|
||||
|
||||
// Exit (success)
|
||||
return $cart;
|
||||
}
|
||||
}
|
||||
} 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 null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Account
|
||||
*
|
||||
* Search for the connected account
|
||||
*
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return account|null The connected account, if found
|
||||
*/
|
||||
public function account(array &$errors = []): ?account
|
||||
{
|
||||
try {
|
||||
if ($result = collection::execute(
|
||||
<<<'AQL'
|
||||
FOR v IN 1..1 OUTBOUND @order GRAPH users
|
||||
FILTER PARSE_IDENTIFIER(v._id).collection == @account
|
||||
LIMIT 1
|
||||
return v
|
||||
AQL,
|
||||
[
|
||||
'order' => $this->document->getId(),
|
||||
'account' => account::COLLECTION
|
||||
],
|
||||
errors: $errors
|
||||
)) {
|
||||
// Found the instance of the account connected to the account
|
||||
|
||||
// Initializing the object
|
||||
$account = new account;
|
||||
|
||||
if (method_exists($account, '__document')) {
|
||||
// Object can implement a document from ArangoDB
|
||||
|
||||
// Implementinf parameters
|
||||
if (isset($result->language)) $result->language = language::{$result->language};
|
||||
if (isset($result->currency)) $result->currency = currency::{$result->currency};
|
||||
|
||||
// Writing the instance of the account document from ArangoDB to the implement object
|
||||
$account->__document($result);
|
||||
|
||||
// Exit (success)
|
||||
return $account;
|
||||
}
|
||||
}
|
||||
} 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 null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ final class session extends core implements document_interface, collection_inter
|
|||
$session->hash = sodium_bin2hex(sodium_crypto_generichash($_id));
|
||||
|
||||
if (document::update($session, errors: $errors)) {
|
||||
// Writed to ArangoDB
|
||||
// Writed into ArangoDB
|
||||
|
||||
// Writing instance of the session document from ArangoDB to the property of the implementing object
|
||||
$this->__document($session);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -79,10 +79,12 @@ trait document
|
|||
/**
|
||||
* Connect
|
||||
*
|
||||
* Searches for a connection document, otherwise creates one
|
||||
*
|
||||
* @param collecton_interface $document Document
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return string|null The identifier of the created edge of the "connect" collection, if created
|
||||
* @return string|null The identifier of the "connect" edge collection, if created or found
|
||||
*/
|
||||
public function connect(collection_interface $document, array &$errors = []): ?string
|
||||
{
|
||||
|
@ -95,8 +97,31 @@ trait document
|
|||
if ($this->document instanceof _document) {
|
||||
// Initialized instance of the document from ArangoDB
|
||||
|
||||
// Writing document and exit (success)
|
||||
return framework_document::write(
|
||||
// Searching for a connection
|
||||
$found = collection::execute(
|
||||
<<<'AQL'
|
||||
FOR d IN @@collection
|
||||
FILTER d._from == @_from && d._to == @_to && d.active == true
|
||||
RETURN d
|
||||
AQL,
|
||||
[
|
||||
'@collection' => connect::COLLECTION,
|
||||
'_from' => $document->getId(),
|
||||
'_to' => $this->document->getId()
|
||||
],
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
if ($found) {
|
||||
// Found the connection document
|
||||
|
||||
// Exit (success)
|
||||
return $found->getId();
|
||||
} else {
|
||||
// Not found the connection document
|
||||
|
||||
// Creting the connection document
|
||||
$created = framework_document::write(
|
||||
connect::COLLECTION,
|
||||
[
|
||||
'_from' => $document->getId(),
|
||||
|
@ -104,6 +129,14 @@ trait document
|
|||
],
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
if ($created) {
|
||||
// Created the connection document
|
||||
|
||||
// Exit (success)
|
||||
return $created;
|
||||
}
|
||||
}
|
||||
} else throw new exception('The instance of the document from ArangoDB is not initialized');
|
||||
} else throw new exception('Failed to initialize ' . $document::TYPE->name . ' collection: ' . $document::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . connect::TYPE->name . ' collection: ' . connect::COLLECTION);
|
||||
|
|
|
@ -21,14 +21,14 @@ use exception;
|
|||
trait disk
|
||||
{
|
||||
/**
|
||||
* Download file from "Yandex Disk"
|
||||
* Download the file from "Yandex Disk"
|
||||
*
|
||||
* @param string $uri URI of the file from "Yandex Disk"
|
||||
* @param string $destination Destination to write the file
|
||||
* @param string|int $name Name for the file
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return array|false The [destination, name, content] of the downloaded file, if the file was downloaded
|
||||
* @return array|false The [destination, name, content] array of the downloaded file, if the file was downloaded
|
||||
*/
|
||||
private static function download(
|
||||
string $uri,
|
||||
|
@ -101,4 +101,52 @@ trait disk
|
|||
// Exit (fail)
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize list of files inside the folder from "Yandex Disk"
|
||||
*
|
||||
* @param string $uri URI of the folder from "Yandex Disk"
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return array|false JSON objects list of files in the folder
|
||||
*/
|
||||
private static function list(string $uri, array &$errors = []): array|false
|
||||
{
|
||||
try {
|
||||
if (!empty($uri)) {
|
||||
// Not empty URI
|
||||
|
||||
// Initializing URL of the file
|
||||
$url = "https://cloud-api.yandex.net/v1/disk/public/resources?public_key=$uri";
|
||||
|
||||
// Checking if the folder is available
|
||||
$session = curl_init($url);
|
||||
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_exec($session);
|
||||
$code = curl_getinfo($session, CURLINFO_RESPONSE_CODE);
|
||||
curl_close($session);
|
||||
|
||||
if ($code === 200) {
|
||||
// The folder is available
|
||||
|
||||
// Downloading the list of files in the folder
|
||||
$files = json_decode(file_get_contents($url));
|
||||
|
||||
// Exit (success)
|
||||
return isset($files?->_embedded?->items) ? $files->_embedded->items : [$files];
|
||||
} else throw new exception("Failed to download list of files inside the folder by link: $uri");
|
||||
} else throw new exception("Empty URI");
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ define('THEME', 'default');
|
|||
|
||||
define('CDEK', require(SETTINGS . DIRECTORY_SEPARATOR . 'deliveries' . DIRECTORY_SEPARATOR . 'cdek.php'));
|
||||
|
||||
define('TELEGRAM_KEY', require(SETTINGS . DIRECTORY_SEPARATOR . 'telegram.php'));
|
||||
|
||||
// Initialize dependencies
|
||||
require ROOT . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
|
||||
|
||||
|
@ -38,9 +40,12 @@ $core->router
|
|||
->write('/cart', new route('cart', 'index', 'cart'), 'GET')
|
||||
->write('/cart/product', new route('cart', 'product', 'cart'), 'PATCH')
|
||||
->write('/cart/summary', new route('cart', 'summary', 'cart'), 'GET')
|
||||
/* ->write('/cart/share', new route('cart', 'share', 'cart'), 'POST') */
|
||||
->write('/cart/share', new route('cart', 'share', 'cart'), 'POST')
|
||||
->write('/cart/attach', new route('cart', 'attach', 'cart'), 'POST')
|
||||
->write('/order/robokassa', new route('cart', 'robokassa', 'cart'), 'GET')
|
||||
->write('/api/robokassa/result', new route('api\acquirings\robokassa', 'result'), 'POST')
|
||||
->write('/robokassa/success', new route('api\acquirings\robokassa', 'success'), 'GET')
|
||||
->write('/robokassa/fail', new route('api\acquirings\robokassa', 'fail'), 'GET')
|
||||
->write('/account/write', new route('account', 'write', 'account'), 'PATCH')
|
||||
->write('/session/write', new route('session', 'write', 'session'), 'PATCH')
|
||||
->write('/session/connect/telegram', new route('session', 'telegram', 'session'), 'PUT')
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// Initializing the closing element
|
||||
const closing = document.getElementById("closing");
|
||||
console.log(closing);
|
||||
// Initializing the closing iterator
|
||||
let iterator =
|
||||
parseInt(closing.style.getPropertyValue("--iterator").replaceAll("'", "")) ||
|
||||
0;
|
||||
console.log(iterator);
|
||||
// Initializing the closing inteval
|
||||
const interval = setInterval(function () {
|
||||
if (iterator-- <= 0) {
|
||||
// Deinitializing the closing inteval
|
||||
clearInterval(interval);
|
||||
|
||||
core.modules.connect("telegram").then(() => {
|
||||
// Imported the telegram module
|
||||
|
||||
// Closing the window
|
||||
core.telegram.api.close();
|
||||
});
|
||||
|
||||
// Exit (success)
|
||||
return;
|
||||
}
|
||||
|
||||
// Writing the iterator into the closing element
|
||||
closing.style.setProperty("--iterator", "'" + iterator + "'");
|
||||
}, 1000);
|
|
@ -17,11 +17,11 @@ class core {
|
|||
// Window
|
||||
static window;
|
||||
|
||||
// The "loading" element
|
||||
static status_loading = document.getElementById("loading");
|
||||
// Account
|
||||
static account;
|
||||
|
||||
// The "account" element
|
||||
static status_account = document.getElementById("account");
|
||||
// The "loading" element
|
||||
static loading = document.getElementById("loading");
|
||||
|
||||
// The <header> element
|
||||
static header = document.body.getElementsByTagName("header")[0];
|
||||
|
|
|
@ -30,10 +30,10 @@ export default class account {
|
|||
* @return {void}
|
||||
*/
|
||||
static authentication() {
|
||||
core.status_loading.removeAttribute("disabled");
|
||||
core.loading.removeAttribute("disabled");
|
||||
|
||||
const timer_for_response = setTimeout(() => {
|
||||
core.status_loading.setAttribute("disabled", true);
|
||||
core.loading.setAttribute("disabled", true);
|
||||
}, 200);
|
||||
|
||||
core.modules.connect("telegram").then(() => {
|
||||
|
@ -44,7 +44,7 @@ export default class account {
|
|||
.request(
|
||||
"/session/connect/telegram",
|
||||
core.telegram.api.initData,
|
||||
'PUT'
|
||||
"PUT",
|
||||
)
|
||||
.then((json) => {
|
||||
if (json) {
|
||||
|
@ -63,20 +63,66 @@ export default class account {
|
|||
// Success (not received errors)
|
||||
|
||||
if (json.connected === true) {
|
||||
core.status_loading.setAttribute("disabled", true);
|
||||
// Deactivating the loading screen
|
||||
core.loading.setAttribute("disabled", true);
|
||||
clearTimeout(timer_for_response);
|
||||
|
||||
const a = core.status_account.getElementsByTagName("a")[0];
|
||||
core.account = {
|
||||
identifier: json.identifier
|
||||
};
|
||||
|
||||
// Initializing the account element
|
||||
const account = document.getElementById("account");
|
||||
|
||||
if (account instanceof HTMLElement) {
|
||||
// Initialized the account element
|
||||
|
||||
// Initializing the account link
|
||||
const a = account.getElementsByTagName("a")[0];
|
||||
|
||||
if (a instanceof HTMLElement) {
|
||||
// Initialized the account link
|
||||
|
||||
a.setAttribute("onclick", "core.account.profile()");
|
||||
a.innerText = json.domain.length > 0
|
||||
? "@" + json.domain
|
||||
: "ERROR";
|
||||
} else {
|
||||
// Not initialized the account link
|
||||
|
||||
if (json.avatar) {
|
||||
// Received the avatar image
|
||||
|
||||
// Initialize the menu button icon
|
||||
const icon = account.getElementsByTagName("i")[0];
|
||||
|
||||
if (icon instanceof HTMLElement) {
|
||||
// Initialized the menu button icon
|
||||
|
||||
setTimeout(function () {
|
||||
// Hiding the menu button icon
|
||||
icon.classList.add("hidden");
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Initializing the avatar image element
|
||||
const image = document.createElement("img");
|
||||
image.setAttribute("src", json.avatar);
|
||||
image.style.setProperty("opacity", "0");
|
||||
image.style.setProperty("--animation-delay", "2s");
|
||||
image.classList.add("opacity", "animated");
|
||||
|
||||
// Writing the avatar image element
|
||||
account.appendChild(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
json.language !== null &&
|
||||
typeof json.language === "string" &&
|
||||
json.langiage.length === 2
|
||||
json.language.length === 2
|
||||
) {
|
||||
core.language = json.language;
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ export default class cart {
|
|||
* @name Write (interface)
|
||||
*
|
||||
* @description
|
||||
* Write the product in the cart
|
||||
* Write the product into the cart
|
||||
*
|
||||
* @param {HTMLButtonElement|HTMLInputElement|null} element Handler elememnt of the product
|
||||
* @param {HTMLElement} product The product element
|
||||
|
@ -388,7 +388,7 @@ export default class cart {
|
|||
static async share(button) {
|
||||
return await core.modules.connect("telegram").then(
|
||||
() => {
|
||||
// Imported the damper module
|
||||
// Imported the telegram module
|
||||
|
||||
// Disabling button
|
||||
button?.setAttribute("disabled", true);
|
||||
|
@ -771,6 +771,24 @@ Object.assign(
|
|||
} else {
|
||||
// Success (not received errors)
|
||||
|
||||
if (json.amount > 0) {
|
||||
// The cart has products
|
||||
|
||||
// Writing the CSS variable of the document element
|
||||
document.documentElement.style.setProperty(
|
||||
"--cart-amount",
|
||||
'"' + json.amount + '"',
|
||||
);
|
||||
} else {
|
||||
// The cart has no products
|
||||
|
||||
// Writing the CSS variable of the document element
|
||||
document.documentElement.style.setProperty(
|
||||
"--cart-amount",
|
||||
"unset",
|
||||
);
|
||||
}
|
||||
|
||||
// Initializing the summary amount <span> element
|
||||
const amount = document.getElementById("amount");
|
||||
|
||||
|
@ -844,7 +862,7 @@ Object.assign(
|
|||
try {
|
||||
// Request
|
||||
return await core.request("/cart/share", undefined, "POST")
|
||||
.then((json) => {
|
||||
.then(async (json) => {
|
||||
if (json) {
|
||||
// Received a JSON-response
|
||||
|
||||
|
@ -863,13 +881,43 @@ Object.assign(
|
|||
if (json.share) {
|
||||
// Received sharing hash
|
||||
|
||||
// Request to the chat-robot
|
||||
core.telegram.api.sendData(
|
||||
// Sending the request to the chat-robot
|
||||
const sended = core.telegram.api.sendData(
|
||||
JSON.stringify({
|
||||
type: "cart_share",
|
||||
hash: json.share,
|
||||
}),
|
||||
);
|
||||
|
||||
if (sended === undefined) {
|
||||
// Failed to send the request
|
||||
|
||||
if (core.account.identifier > 0) {
|
||||
// Initialized the account identifier
|
||||
|
||||
// Request
|
||||
return await core.request(
|
||||
"/cart/attach",
|
||||
'share=' + json.share,
|
||||
"POST",
|
||||
).then((json) => {
|
||||
if (json) {
|
||||
// Received a JSON-response
|
||||
|
||||
if (json.success) {
|
||||
// Received the success status
|
||||
|
||||
core.modules.connect("telegram").then(() => {
|
||||
// Imported the telegram module
|
||||
|
||||
// Closing the Telegram Mini App
|
||||
core.telegram.api.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if (json.robokassa) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -15,6 +15,11 @@ export default class loader {
|
|||
*/
|
||||
static type = "module";
|
||||
|
||||
/**
|
||||
* @name Actial URI
|
||||
*/
|
||||
static uri;
|
||||
|
||||
/**
|
||||
* @name Load
|
||||
*
|
||||
|
@ -23,7 +28,7 @@ export default class loader {
|
|||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
static async load(uri = "/", body) {
|
||||
static async load(uri = "/", body, back = false) {
|
||||
if (typeof uri === "string") {
|
||||
// Received and validated uri
|
||||
|
||||
|
@ -54,8 +59,27 @@ export default class loader {
|
|||
} else {
|
||||
// Success (not received errors)
|
||||
|
||||
if (back) {
|
||||
// Requested to go back
|
||||
|
||||
// Writing actual URI
|
||||
this.uri = history.state?.previous;
|
||||
|
||||
// Deletimg from the browser history
|
||||
history.back();
|
||||
} else {
|
||||
// Requested to go forward
|
||||
|
||||
// Writing to the browser history
|
||||
history.pushState({}, json.title ?? uri, uri);
|
||||
history.pushState(
|
||||
{ previous: this.uri },
|
||||
json.title ?? uri,
|
||||
uri,
|
||||
);
|
||||
|
||||
// Writing actual URI
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <title>
|
||||
|
@ -222,7 +246,10 @@ export default class loader {
|
|||
// 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.nextElementSibling);
|
||||
body.insertBefore(
|
||||
core.main,
|
||||
section.nextElementSibling,
|
||||
);
|
||||
} else {
|
||||
// Not found section elements <section> inside the <body> element
|
||||
|
||||
|
|
|
@ -21,6 +21,11 @@ export default class telegram {
|
|||
* @see {@link https://core.telegram.org/bots/webapps#initializing-mini-apps}
|
||||
*/
|
||||
static api = window.Telegram.WebApp;
|
||||
|
||||
/**
|
||||
* @name List of BackButton events
|
||||
*/
|
||||
static back = [];
|
||||
}
|
||||
|
||||
// Connecting to the core
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
core.modules.connect(["telegram"])
|
||||
.then(() => {
|
||||
//
|
||||
// Imported the telegram module
|
||||
|
||||
// Expanding the "Web App" window
|
||||
// core.telegram.api.expand();
|
||||
core.telegram.api.expand();
|
||||
|
||||
// Writing settings for the "Web App" window
|
||||
core.telegram.api.enableVerticalSwipes();
|
||||
|
@ -24,6 +24,20 @@ core.modules.connect(["telegram"])
|
|||
document.documentElement.style.setProperty('--tg-theme-section-bg-color', 'var(--tg-theme-secondary-bg-color)');
|
||||
}
|
||||
|
||||
if (core.telegram.back.length > 0) {
|
||||
// Initialized BackButton events
|
||||
|
||||
// Initializing the "Back Button" of the "Web App" window
|
||||
core.telegram.api.BackButton.show();
|
||||
|
||||
for (const event of core.telegram.back) {
|
||||
// Iterating over BackButton events
|
||||
|
||||
// Initializing the BackButton event listener
|
||||
core.telegram.api.BackButton.onClick(event);
|
||||
}
|
||||
}
|
||||
|
||||
core.modules.connect(["session", "account"])
|
||||
.then(() => {
|
||||
//
|
||||
|
|
|
@ -11,9 +11,9 @@ use mirzaev\huesos\controllers\core as controller,
|
|||
mirzaev\huesos\models\telegram;
|
||||
|
||||
// Framework for Telegram
|
||||
use Zanzara\Zanzara,
|
||||
Zanzara\Context,
|
||||
Zanzara\Config;
|
||||
use Zanzara\Zanzara as zanzara,
|
||||
Zanzara\Context as context,
|
||||
Zanzara\Config as config;
|
||||
|
||||
// Framework for ArangoDB
|
||||
use mirzaev\arangodb\document;
|
||||
|
@ -34,6 +34,9 @@ define('STORAGE', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '
|
|||
// Файл в формате xlsx с примером excel-документа для импорта каталога
|
||||
define('CATALOG_EXAMPLE', STORAGE . DIRECTORY_SEPARATOR . 'example.xlsx');
|
||||
|
||||
// Файл
|
||||
define('GREETING_VIDEO', STORAGE . DIRECTORY_SEPARATOR . 'greeting.mp4');
|
||||
|
||||
// Файл в формате xlsx для импорта каталога
|
||||
define('CATALOG_IMPORT', STORAGE . DIRECTORY_SEPARATOR . 'import.xlsx');
|
||||
|
||||
|
@ -59,15 +62,15 @@ require __DIR__ . DIRECTORY_SEPARATOR
|
|||
// Инициализация ядра моделей MINIMAL
|
||||
new model(true);
|
||||
|
||||
$config = new Config();
|
||||
$config->setParseMode(Config::PARSE_MODE_MARKDOWN);
|
||||
$config = new config();
|
||||
$config->setParseMode(config::PARSE_MODE_MARKDOWN);
|
||||
$config->useReactFileSystem(true);
|
||||
|
||||
$bot = new Zanzara(TELEGRAM_KEY, $config);
|
||||
$robot = new zanzara(TELEGRAM_KEY, $config);
|
||||
|
||||
$bot->onUpdate(function (Context $ctx): void {
|
||||
$robot->onUpdate(function (context $context): void {
|
||||
// Initializing the message
|
||||
$message = $ctx->getMessage();
|
||||
$message = $context->getMessage();
|
||||
|
||||
// Initializing the "web app" data
|
||||
$app = $message?->getWebAppData();
|
||||
|
@ -82,13 +85,13 @@ $bot->onUpdate(function (Context $ctx): void {
|
|||
// Cart attaching
|
||||
|
||||
// Attaching cart to the Telegram account
|
||||
telegram::cart_attach($ctx, $request->hash);
|
||||
telegram::cart_attach($context, $request->hash);
|
||||
}
|
||||
} else {
|
||||
// Not initialized the "web app" data
|
||||
|
||||
// Initializing account
|
||||
$account = $ctx->get('account');
|
||||
$account = $context->get('account');
|
||||
|
||||
if ($account) {
|
||||
// Initialized the account
|
||||
|
@ -117,7 +120,9 @@ $bot->onUpdate(function (Context $ctx): void {
|
|||
if (document::update($account->__document())) {
|
||||
// Writed the account instance into the ArangoDB document
|
||||
|
||||
$ctx->sendMessage(
|
||||
/*
|
||||
// Sending the message
|
||||
$context->sendMessage(
|
||||
<<<TXT
|
||||
✅ *SIM\-номер зарегистрирован:* $sanitized
|
||||
TXT,
|
||||
|
@ -126,17 +131,34 @@ $bot->onUpdate(function (Context $ctx): void {
|
|||
'remove_keyboard' => true
|
||||
]
|
||||
]
|
||||
)->then(function ($message) use ($ctx) {
|
||||
)->then(function ($message) use ($context) {
|
||||
// Sended message
|
||||
|
||||
// Sending the account parameters menu
|
||||
telegram::account_parameters($ctx);
|
||||
telegram::account_parameters($context);
|
||||
}); */
|
||||
|
||||
// Sending the message
|
||||
$context->sendMessage(
|
||||
<<<TXT
|
||||
✅ *Номер телефона зарегистрирован:* $sanitized
|
||||
TXT,
|
||||
[
|
||||
'reply_markup' => [
|
||||
'remove_keyboard' => true
|
||||
]
|
||||
]
|
||||
)->then(function ($message) use ($context) {
|
||||
// Sended message
|
||||
|
||||
// Sending the account parameters menu
|
||||
telegram::account_parameters($context);
|
||||
});
|
||||
} else {
|
||||
// Not writed the account instance into the ArangoDB document
|
||||
|
||||
// Sending the message
|
||||
$ctx->sendMessage('⚠️ *Не удалось записать SIM\-номер*');
|
||||
$context->sendMessage('⚠️ *Не удалось записать SIM\-номер*');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,42 +169,71 @@ $bot->onUpdate(function (Context $ctx): void {
|
|||
unset($app);
|
||||
});
|
||||
|
||||
$bot->onCommand('start', fn($ctx) => telegram::start($ctx));
|
||||
$bot->onCommand('contacts', fn($ctx) => telegram::contacts($ctx));
|
||||
$bot->onCommand('company', fn($ctx) => telegram::company($ctx));
|
||||
$bot->onCommand('community', fn($ctx) => telegram::community($ctx));
|
||||
$bot->onCommand('settings', fn($ctx) => telegram::settings($ctx));
|
||||
$robot->onCommand('start', fn(context $context) => telegram::start($context));
|
||||
$robot->onCommand('contacts', fn(context $context) => telegram::contacts($context));
|
||||
$robot->onCommand('company', fn(context $context) => telegram::company($context));
|
||||
$robot->onCommand('community', fn(context $context) => telegram::community($context));
|
||||
$robot->onCommand('settings', fn(context $context) => telegram::settings($context));
|
||||
|
||||
$bot->onText('💬 Контакты', fn($ctx) => telegram::contacts($ctx));
|
||||
$bot->onText('🏛️ О компании', fn($ctx) => telegram::company($ctx));
|
||||
$bot->onText('🎯 Сообщество', fn($ctx) => telegram::community($ctx));
|
||||
$bot->onText('⚙️ Настройки', fn($ctx) => telegram::settings($ctx));
|
||||
$robot->onText('💬 Контакты', fn(context $context) => telegram::contacts($context));
|
||||
$robot->onText('🏛️ О компании', fn(context $context) => telegram::company($context));
|
||||
$robot->onText('🎯 Сообщество', fn(context $context) => telegram::community($context));
|
||||
$robot->onText('⚙️ Настройки', fn(context $context) => telegram::settings($context));
|
||||
|
||||
$bot->onCbQueryData(['mail'], fn($ctx) => telegram::_mail($ctx));
|
||||
$bot->onCbQueryData(['import_request'], fn($ctx) => telegram::import_request($ctx));
|
||||
$bot->onCbQueryData(['order'], fn($ctx) => telegram::order($ctx));
|
||||
$bot->onCbQueryData(['tuning'], fn($ctx) => telegram::tuning($ctx));
|
||||
$bot->onCbQueryData(['brands'], fn($ctx) => telegram::brands($ctx));
|
||||
$robot->onCbQueryData(['contacts'], fn(context $context) => telegram::contacts($context));
|
||||
$robot->onCbQueryData(['company'], fn(context $context) => telegram::company($context));
|
||||
$robot->onCbQueryData(['community'], fn(context $context) => telegram::community($context));
|
||||
$robot->onCbQueryData(['settings'], fn(context $context) => telegram::settings($context));
|
||||
|
||||
$bot->onCbQueryData(['receiver_sim_choose'], fn($ctx) => telegram::receiver_sim_choose($ctx));
|
||||
$bot->onCbQueryData(['receiver_sim_request'], fn($ctx) => telegram::receiver_sim_request($ctx));
|
||||
$bot->onCbQueryData(['receiver_sim_input'], fn($ctx) => telegram::receiver_sim_input($ctx));
|
||||
$bot->onCbQueryData(['receiver_sim_write'], fn($ctx) => telegram::receiver_sim_write($ctx));
|
||||
$robot->onCbQueryData(['mail'], fn(context $context) => telegram::_mail($context));
|
||||
$robot->onCbQueryData(['import_request'], fn(context $context) => telegram::import_request($context));
|
||||
|
||||
$bot->onCbQueryData(['receiver_name_choose'], fn($ctx) => telegram::receiver_name_choose($ctx));
|
||||
$bot->onCbQueryData(['receiver_name_request'], fn($ctx) => telegram::receiver_name_request($ctx));
|
||||
$bot->onCbQueryData(['receiver_name_input'], fn($ctx) => telegram::receiver_name_input($ctx));
|
||||
$bot->onCbQueryData(['receiver_name_write'], fn($ctx) => telegram::receiver_name_write($ctx));
|
||||
$robot->onCbQueryData(['order_commentary_request'], fn(context $context) => telegram::order_commentary_request($context));
|
||||
$robot->onCbQueryData(['order'], fn(context $context) => telegram::order($context));
|
||||
|
||||
$bot->onException(function (Context $ctx, $exception) {
|
||||
$robot->onCbQueryData(['tuning'], fn(context $context) => telegram::tuning($context));
|
||||
$robot->onCbQueryData(['brands'], fn(context $context) => telegram::brands($context));
|
||||
|
||||
$robot->onCbQueryData(['cart_delivery'], fn(context $context) => telegram::cart_delivery($context));
|
||||
|
||||
$robot->onCbQueryData(['delivery_registration_destination_office'], fn(context $context) => telegram::delivery_registration_destination_office($context));
|
||||
$robot->onCbQueryData(['delivery_registration_destination_door'], fn(context $context) => telegram::delivery_registration_destination_door($context));
|
||||
$robot->onCbQueryData(['delivery_registration_sim_input'], fn(context $context) => telegram::delivery_registration_sim_input($context));
|
||||
$robot->onCbQueryData(['delivery_registration_name_input'], fn(context $context) => telegram::delivery_registration_name_input($context));
|
||||
$robot->onCbQueryData(['delivery_registration_address_input'], fn(context $context) => telegram::delivery_registration_address_input($context));
|
||||
$robot->onCbQueryData(['delivery_registration'], fn(context $context) => telegram::delivery_registration($context));
|
||||
|
||||
$robot->onCbQueryData(['account_parameters_force'], fn(context $context) => telegram::account_parameters($context, force: true));
|
||||
|
||||
$robot->onCbQueryData(['receiver_sim_choose'], fn(context $context) => telegram::receiver_sim_choose($context));
|
||||
$robot->onCbQueryData(['receiver_sim_request'], fn(context $context) => telegram::receiver_sim_request($context));
|
||||
/* $robot->onCbQueryData(['receiver_sim_input'], fn(context $context) => telegram::receiver_sim_input($context)); */
|
||||
$robot->onCbQueryData(['receiver_sim_write'], fn(context $context) => telegram::receiver_sim_write($context));
|
||||
|
||||
$robot->onCbQueryData(['receiver_name_choose'], fn(context $context) => telegram::receiver_name_choose($context));
|
||||
$robot->onCbQueryData(['receiver_name_request'], fn(context $context) => telegram::receiver_name_request($context));
|
||||
/* $robot->onCbQueryData(['receiver_name_input'], fn(context $context) => telegram::receiver_name_input($context)); */
|
||||
$robot->onCbQueryData(['receiver_name_write'], fn(context $context) => telegram::receiver_name_write($context));
|
||||
|
||||
$robot->onCbQueryData(['receiver_destination_choose'], fn(context $context) => telegram::receiver_destination_choose($context));
|
||||
$robot->onCbQueryData(['receiver_destination_office'], fn(context $context) => telegram::receiver_destination_office($context));
|
||||
$robot->onCbQueryData(['receiver_destination_door'], fn(context $context) => telegram::receiver_destination_door($context));
|
||||
|
||||
$robot->onCbQueryData(['receiver_address_choose'], fn(context $context) => telegram::receiver_address_choose($context));
|
||||
$robot->onCbQueryData(['receiver_address_request_office'], fn(context $context) => telegram::receiver_address_request($context, office: true));
|
||||
$robot->onCbQueryData(['receiver_address_request_door'], fn(context $context) => telegram::receiver_address_request($context, office: false));
|
||||
/* $robot->onCbQueryData(['receiver_address_input'], fn(context $context) => telegram::receiver_address_input($context)); */
|
||||
$robot->onCbQueryData(['receiver_address_write'], fn(context $context) => telegram::receiver_address_write($context));
|
||||
|
||||
$robot->onException(function (Context $context, $exception) {
|
||||
var_dump($exception);
|
||||
});
|
||||
|
||||
// Инициализация middleware с обработкой аккаунта
|
||||
$bot->middleware([telegram::class, "account"]);
|
||||
$robot->middleware([telegram::class, "account"]);
|
||||
|
||||
// Инициализация middleware с обработкой технических работ разных уровней
|
||||
$bot->middleware([telegram::class, "suspension"]);
|
||||
$robot->middleware([telegram::class, "suspension"]);
|
||||
|
||||
// Запуск чат-робота
|
||||
$bot->run();
|
||||
$robot->run();
|
||||
|
|
|
@ -9,3 +9,11 @@ section#account {
|
|||
display: flex;
|
||||
}
|
||||
|
||||
nav#menu>a#account>img {
|
||||
position: absolute;
|
||||
width: auto;
|
||||
height: inherit;
|
||||
object-fit: contain;
|
||||
border-radius: 100%;
|
||||
/* border: 2px solid var(--tg-theme-bottom-bar-bg-color); */
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
main {
|
||||
--offset-bottom: 2rem;
|
||||
justify-content: center;
|
||||
gap: unset;
|
||||
}
|
||||
|
||||
main>:is(:first-child, :last-child) {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
main>h2 {
|
||||
margin: unset;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 1.2rem;
|
||||
color: var(--tg-theme-accent-text-color);
|
||||
}
|
||||
|
||||
main>h2+small {
|
||||
margin-bottom: calc(0 - var(--offset-bottom));
|
||||
color: var(--tg-theme-subtitle-text-color);
|
||||
}
|
||||
|
||||
main>#closing {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 0.8rem;
|
||||
margin-bottom: var(--offset-bottom);
|
||||
}
|
||||
|
||||
main>#closing>i.icon:first-child {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
main>#closing:after {
|
||||
content: ': ' var(--iterator);
|
||||
font-weight: bold;
|
||||
color: var(--tg-theme-accent-text-color);
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
main {
|
||||
--offset-bottom: 2rem;
|
||||
justify-content: center;
|
||||
gap: unset;
|
||||
}
|
||||
|
||||
main>:is(:first-child, :last-child) {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
main>h2 {
|
||||
margin: unset;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 1.2rem;
|
||||
color: var(--tg-theme-accent-text-color);
|
||||
}
|
||||
|
||||
main>h2+small {
|
||||
margin-bottom: calc(0 - var(--offset-bottom));
|
||||
color: var(--tg-theme-subtitle-text-color);
|
||||
}
|
||||
|
||||
main>#closing {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 0.8rem;
|
||||
margin-bottom: var(--offset-bottom);
|
||||
}
|
||||
|
||||
main>#closing>i.icon:first-child {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
main>#closing:after {
|
||||
content: ': ' var(--iterator);
|
||||
font-weight: bold;
|
||||
color: var(--tg-theme-accent-text-color);
|
||||
}
|
|
@ -1,5 +1,24 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
@keyframes slide-down {
|
||||
0% {
|
||||
transform: translate(0, -100%);
|
||||
clip-path: polygon(0% 100%, 100% 100%, 100% 200%, 0% 200%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(0, 0%);
|
||||
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
|
||||
}
|
||||
}
|
||||
|
||||
.slide.down.animated {
|
||||
animation-duration: var(--animation-duration, 0.25s);
|
||||
animation-name: slide-down;
|
||||
animation-fill-mode: forwards;
|
||||
animation-timing-function: var(--animatiom-timing, ease-out);
|
||||
}
|
||||
|
||||
@keyframes slide-down-revert {
|
||||
0% {
|
||||
transform: translate(0, 0%);
|
||||
|
@ -13,8 +32,26 @@
|
|||
}
|
||||
|
||||
.slide.down.revert.animated {
|
||||
animation-duration: var(--animation-duration, 0.2s);
|
||||
animation-duration: var(--animation-duration, 0.25s);
|
||||
animation-name: slide-down-revert;
|
||||
animation-fill-mode: forwards;
|
||||
animation-timing-function: cubic-bezier(1, 0, 1, 1);
|
||||
animation-timing-function: var(--animatiom-timing, ease-in);
|
||||
}
|
||||
|
||||
@keyframes opacity {
|
||||
0% {
|
||||
opacity: 0%;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.opacity.animated {
|
||||
animation-duration: var(--animation-duration, 0.25s);
|
||||
animation-name: opacity;
|
||||
animation-fill-mode: forwards;
|
||||
animation-timing-function: var(--animatiom-timing, ease-in);
|
||||
animation-delay: var(--animation-delay, 0s);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,10 @@ main>section:is(#summary, #products, #delivery, #delivery_request) {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
main:not(:has(h2#title + section#delivery))>h2#title {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
main>section:is(#summary, #delivery) {
|
||||
background-color: var(--tg-theme-section-bg-color);
|
||||
}
|
||||
|
@ -245,12 +249,13 @@ main>section#products>article.product>a {
|
|||
}
|
||||
|
||||
main>section#products>article.product>a>img:first-of-type {
|
||||
/* width: 5rem; */
|
||||
min-width: 5rem;
|
||||
width: 7rem;
|
||||
/* min-width: 5rem; */
|
||||
min-height: 100%;
|
||||
object-fit: cover;
|
||||
image-rendering: auto;
|
||||
border-radius: 0.75rem;
|
||||
border: 0.2rem solid var(--tg-theme-hint-color);
|
||||
box-shadow: -5px 0px 50px 10px rgba(0, 0, 0, 0.4);
|
||||
-webkit-box-shadow: -5px 0px 50px 10px rgba(0, 0, 0, 0.4);
|
||||
-moz-box-shadow: -5px 0px 50px 10px rgba(0, 0, 0, 0.4);
|
||||
|
@ -276,12 +281,17 @@ main>section#products>article.product>div>div.head {
|
|||
gap: 1rem;
|
||||
}
|
||||
|
||||
main>section#products>article.product>div>div.head>button {
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
main>section#products>article.product>div>div>button:first-of-type {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
main>section#products>article.product>div>div>button {
|
||||
padding: 0.4rem;
|
||||
color: var(--tg-theme-text-color);
|
||||
background: unset;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,66 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
main>section#categories {
|
||||
main>section#categories:has(a),
|
||||
main>section#categories:not(:has(a))>ul:has(li.category[type="button"]) {
|
||||
width: var(--width);
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
flex-flow: var(--catalog-categories-wrap-flex-wrap, row wrap);
|
||||
flex-direction: var(--catalog-categories-wrap-flex-direction, 'row');
|
||||
}
|
||||
|
||||
main>section#categories:has(a) {
|
||||
gap: var(--gap, 5px);
|
||||
}
|
||||
|
||||
main>section#categories>a.category[type="button"] {
|
||||
main>section#categories:not(:has(a))>ul:has(li.category[type="button"])>ul {
|
||||
margin-bottom: var(--gap, 5px);
|
||||
}
|
||||
|
||||
main>section#categories ul {
|
||||
padding-left: 1.3rem;
|
||||
list-style-type: none;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
main>section#categories>li {
|
||||
line-height: 1rem;
|
||||
}
|
||||
|
||||
main>section#categories li {
|
||||
/* font-family: "Chalet"; */
|
||||
}
|
||||
|
||||
main>section#categories li.openable {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
main>section#categories ul>li:has(+ ul) {
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
main>section#categories ul>ul {
|
||||
--offset: 1rem;
|
||||
--padding: 1rem;
|
||||
margin-top: calc(var(--offset, 1rem) * -1);
|
||||
padding-top: calc(var(--offset, 1rem) + var(--padding, 1rem));
|
||||
padding-bottom: var(--padding, 1rem);
|
||||
border-radius: 0 0 0.75rem 0.75rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.3rem;
|
||||
background: var(--catalog-categories-buttons-lists-background-color, var(--tg-theme-secondary-bg-color));
|
||||
}
|
||||
|
||||
main>section#categories:not(:has(a))>ul:has(li.category[type="button"]) {
|
||||
margin: unset;
|
||||
padding: unset;
|
||||
}
|
||||
|
||||
main>section#categories>a.category[type="button"],
|
||||
main>section#categories>ul>li.category[type="button"] {
|
||||
--padding: 0.7rem;
|
||||
z-index: unset;
|
||||
position: relative;
|
||||
height: 23px;
|
||||
padding: var(--padding);
|
||||
|
@ -19,21 +71,54 @@ main>section#categories>a.category[type="button"] {
|
|||
overflow: hidden;
|
||||
border-radius: 0.75rem;
|
||||
color: var(--tg-theme-button-text-color);
|
||||
background-color: var(--tg-theme-button-color);
|
||||
background: var(--catalog-categories-buttons-background);
|
||||
}
|
||||
|
||||
main>section#categories>a.category[type="button"]:not(.column):after,
|
||||
main>section#categories>ul>li.category[type="button"]:not(.column):after {
|
||||
z-index: -100;
|
||||
position: absoulute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
filter: brightness(0.8);
|
||||
background-color: var(--tg-theme-button-color);
|
||||
}
|
||||
|
||||
main>section#categories:last-child {
|
||||
/* margin-bottom: unset; */
|
||||
}
|
||||
|
||||
main>section#categories>a.category[type="button"]:has(>img) {
|
||||
/* 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"],
|
||||
main>section#categories>ul>li.category[type="button"] {
|
||||
width: var(--catalog-categories-buttons-width, calc(50% - var(--padding) * 2));
|
||||
height: var(--catalog-categories-buttons-height, 180px);
|
||||
padding: unset;
|
||||
}
|
||||
|
||||
main>section#categories>a.category[type="button"]>img {
|
||||
main>section#categories ul>li[type="button"].opened+ul {
|
||||
max-height: var(--catalog-categories-buttons-lists-height, 500px);
|
||||
transition: max-height 0.15s ease-in 0.1s, margin 0s ease-out 0.1s, padding 0s ease-out 0.1s;
|
||||
}
|
||||
|
||||
main>section#categories ul>li[type="button"]:not(.opened)+ul {
|
||||
max-height: 0;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
margin-top: 0;
|
||||
transition: max-height 0.25s ease-out, margin 0s ease-out 0.25s, padding 0s ease-out 0.25s;
|
||||
}
|
||||
|
||||
main>section#categories>a.category[type="button"]>img,
|
||||
main>section#categories>ul>li.category[type="button"]>img {
|
||||
position: absolute;
|
||||
left: -5%;
|
||||
top: -5%;
|
||||
|
@ -41,35 +126,89 @@ main>section#categories>a.category[type="button"]>img {
|
|||
height: 110%;
|
||||
object-fit: cover;
|
||||
/* filter: blur(1px); */
|
||||
filter: brightness(60%);
|
||||
filter: var(--catalog-categories-buttons-images-filter, contrast(1.2) brightness(60%));
|
||||
}
|
||||
|
||||
main>section#categories>a.category[type="button"]:is(:hover, :focus)>img {
|
||||
main>section#categories>a.category[type="button"]>img.right,
|
||||
main>section#categories>ul>li.category[type="button"]>img.right {
|
||||
/* position: absolute; */
|
||||
left: unset;
|
||||
top: unset;
|
||||
right: 0;
|
||||
width: calc(100% - var(--catalog-categories-buttons-texts-width, 10rem));
|
||||
}
|
||||
|
||||
main>section#categories>a.category[type="button"]:is(:hover, :focus)>img,
|
||||
main>section#categories>ul>li.category[type="button"]:is(:hover, :focus)>img {
|
||||
filter: unset;
|
||||
}
|
||||
|
||||
main>section#categories>a.category[type="button"]:has(>img)>p {
|
||||
/* main>section#categories>a.category[type="button"]:has(>img)>p { */
|
||||
main>section#categories>a.category[type="button"]>p,
|
||||
main>section#categories>ul>li.category[type="button"]>p {
|
||||
position: absolute;
|
||||
left: var(--padding);
|
||||
bottom: var(--padding);
|
||||
top: var(--catalog-categories-buttons-texts-top, var(--padding));
|
||||
bottom: var(--catalog-categories-buttons-texts-bottom, var(--padding));
|
||||
right: var(--padding);
|
||||
margin: unset;
|
||||
width: min-content;
|
||||
width: calc(var(--catalog-categories-buttons-texts-width, 10rem) - 0.7rem * 2);
|
||||
border-radius: 0.75rem;
|
||||
background: var(--tg-theme-section-bg-color);
|
||||
box-shadow: 0px 0px 8px 4px rgba(0, 0, 0, 0.3);
|
||||
-webkit-box-shadow: 0px 0px 8px 4px rgba(0, 0, 0, 0.3);
|
||||
-moz-box-shadow: 0px 0px 8px 4px rgba(0, 0, 0, 0.3);
|
||||
color: var(--catalog-categories-buttons-texts-title-color, var(--tg-theme-text-color));
|
||||
/* background: var(--tg-theme-hint-color); */
|
||||
}
|
||||
|
||||
main>section#categories>a.category[type="button"]>p {
|
||||
main>section#categories>a.category[type="button"]>div.separator.gradient:has(+img.right),
|
||||
main>section#categories>ul>li.category[type="button"]>div.separator.gradient:has(+img.right) {
|
||||
--background: var(--catalog-categories-buttons-background, var(--tg-theme-button-color));
|
||||
z-index: 100;
|
||||
padding: 8px 16px;
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: calc(100% - var(--catalog-categories-buttons-texts-width, 10rem));
|
||||
height: 100%;
|
||||
background: var(--background);
|
||||
background: linear-gradient(90deg, var(--background) 0%, transparent var(--catalog-categories-buttons-separator-width, 100%));
|
||||
}
|
||||
|
||||
main>section#categories>a.category[type="button"]>p,
|
||||
main>section#categories>ul>li.category[type="button"]>p {
|
||||
z-index: 100;
|
||||
padding: 0 calc(var(--padding) / 2);
|
||||
}
|
||||
|
||||
main>section#categories>a.category[type="button"]>p:not(.background),
|
||||
main>section#categories>ul>li.category[type="button"]>p:not(.background) {
|
||||
font-family: "Cygre";
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
main>section#categories>a.category[type="button"]>p.background,
|
||||
main>section#categories>ul>li.category[type="button"]>p.background {
|
||||
padding: 8px calc(var(--padding, 0.7rem) * 1.3);
|
||||
width: min-content;
|
||||
}
|
||||
|
||||
main>section#categories>a.category[type="button"]>p.background:after,
|
||||
main>section#categories>ul>li.category[type="button"]>p.background:after {
|
||||
z-index: -100;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 0.75rem;
|
||||
background-color: var(--tg-theme-button-color);
|
||||
box-shadow: 0px 0px 8px 4px rgba(0, 0, 0, 0.1);
|
||||
-webkit-box-shadow: 0px 0px 8px 4px rgba(0, 0, 0, 0.1);
|
||||
-moz-box-shadow: 0px 0px 8px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
main>section#filters {
|
||||
--filters-height: 2rem;
|
||||
width: var(--width);
|
||||
max-height: 2.5rem;
|
||||
max-height: var(--filters-height);
|
||||
display: flex;
|
||||
align-items: start;
|
||||
}
|
||||
|
@ -117,9 +256,11 @@ main>section#products>div.column>article.product:not(:is(:hover, :focus))>* {
|
|||
}
|
||||
|
||||
main>section#products>div.column>article.product>a>img:first-of-type {
|
||||
width: 100%;
|
||||
--border: 0.2rem;
|
||||
width: calc(100% - var(--border) * 2);
|
||||
image-rendering: auto;
|
||||
border-radius: 0.75rem;
|
||||
border: 0.2rem solid var(--tg-theme-hint-color);
|
||||
box-shadow: 0px 5px 70px 20px rgba(0, 0, 0, 0.3);
|
||||
-webkit-box-shadow: 0px 5px 70px 20px rgba(0, 0, 0, 0.3);
|
||||
-moz-box-shadow: 0px 5px 70px 20px rgba(0, 0, 0, 0.3);
|
||||
|
@ -137,9 +278,14 @@ main>section#products>div.column>article.product>a>p.title {
|
|||
font-weight: bold;
|
||||
overflow-wrap: anywhere;
|
||||
hyphens: auto;
|
||||
color: var(--tg-theme-text-color);
|
||||
}
|
||||
|
||||
main>section#products>div.column>article.product>a>p.title>span {
|
||||
margin-top: 0.2rem;
|
||||
display: block;
|
||||
font-weight: 400;
|
||||
font-size: small;
|
||||
color: var(--tg-theme-hint-color);
|
||||
}
|
||||
|
||||
|
@ -153,7 +299,8 @@ main>section#products>div.column>article.product>div[data-product="buttons"]:las
|
|||
border-radius: 0.75rem;
|
||||
}
|
||||
|
||||
main>section#products>div.column>article.product[data-product-amount]:not(:is([data-product-amount="0"], [data-product-amount="1"]))>div[data-product="buttons"]:last-of-type {
|
||||
/* main>section#products>div.column>article.product[data-product-amount]:not(:is([data-product-amount="0"], [data-product-amount="1"]))>div[data-product="buttons"]:last-of-type { */
|
||||
main>section#products>div.column>article.product[data-product-amount]:not(:is([data-product-amount="0"]))>div[data-product="buttons"]:last-of-type {
|
||||
container-type: inline-size;
|
||||
container-name: product-buttons;
|
||||
}
|
||||
|
@ -161,9 +308,11 @@ main>section#products>div.column>article.product[data-product-amount]:not(:is([d
|
|||
main>section#products>div.column>article.product>div[data-product="buttons"]>button[data-product-button="toggle"] {
|
||||
padding: 0;
|
||||
flex-grow: 1;
|
||||
background-color: var(--catalog-button-cart-background, var(--tg-theme-button-color));
|
||||
}
|
||||
|
||||
main>section#products>div.column>article.product:is([data-product-amount="0"], [data-product-amount="1"])>div[data-product="buttons"]>button[data-product-button="toggle"]>span[data-product-parameter="amount"],
|
||||
/* main>section#products>div.column>article.product:is([data-product-amount="0"], [data-product-amount="1"])>div[data-product="buttons"]>button[data-product-button="toggle"]>span[data-product-parameter="amount"], */
|
||||
main>section#products>div.column>article.product:is([data-product-amount="0"])>div[data-product="buttons"]>button[data-product-button="toggle"]>span[data-product-parameter="amount"],
|
||||
main>section#products>div.column>article.product[data-product-amount="0"]>div[data-product="buttons"]>button:is([data-product-button="write"], [data-product-button="delete"]) {
|
||||
display: none;
|
||||
}
|
||||
|
@ -173,8 +322,17 @@ main>section#products>div.column>article.product>div[data-product="buttons"]>but
|
|||
margin: 0 0.2rem;
|
||||
}
|
||||
|
||||
main>section#products>div.column>article.product[data-product-amount]:not([data-product-amount="0"])>div[data-product="buttons"]>button[data-product-button="toggle"] {
|
||||
background-color: var(--catalog-button-cart-added-background, var(--tg-theme-button-color));
|
||||
}
|
||||
|
||||
main>section#products>div.column>article.product[data-product-amount]:not([data-product-amount="0"])>div[data-product="buttons"] {
|
||||
filter: hue-rotate(calc(120deg + var(--hue-rotate-offset, 0deg)));
|
||||
/* hehe */
|
||||
filter: var(--catalog-button-cart-added-background, hue-rotate(calc(120deg + var(--hue-rotate-offset, 0deg))));
|
||||
}
|
||||
|
||||
main>section#products>div.column>article.product>div[data-product="buttons"]>button {
|
||||
background-color: var(--catalog-button-cart-added-background, var(--tg-theme-button-color));
|
||||
}
|
||||
|
||||
@container product-buttons (max-width: 200px) {
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
main>section#categories ul>ul {
|
||||
--padding: 1rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
main>section#categories>ul ul>li {
|
||||
--padding-row: calc(var(--gap, 5px) * 1);
|
||||
position: relative;
|
||||
padding: var(--padding-row);
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
main>section#categories>ul ul>li>:is(small, i.icon):last-child {
|
||||
margin-left: auto;
|
||||
margin-right: 0.1rem;
|
||||
rotate: 0deg;
|
||||
transition: rotate 0.1s ease-out;
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
main>section#categories>ul ul>li.opened>:is(small, i.icon):last-child {
|
||||
rotate: 90deg;
|
||||
transition: rotate 0.1s ease-in;
|
||||
}
|
||||
|
||||
main>section#categories>ul ul>li:after,
|
||||
main>section#categories>ul ul>li[type="button"].opened+ul:has(+ li)>li:last-of-type:after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
bottom: 0px;
|
||||
width: calc(100% - var(--padding-row) * 2);
|
||||
display: block;
|
||||
align-self: center;
|
||||
border-bottom: 1px solid var(--catalog-categories-buttons-lists-separatpr-color, var(--tg-theme-hint-color));
|
||||
transition: bottom 0s linear 0s;
|
||||
}
|
||||
|
||||
main>section#categories>ul ul>li:last-of-type:not(.opened):after {
|
||||
bottom: -1rem;
|
||||
transition: bottom 0.1s ease-out 0.25s;
|
||||
}
|
||||
|
||||
main>section#categories>ul ul>li:last-of-type.opened:after {
|
||||
bottom: -1px;
|
||||
transition: bottom 0.1s ease-in;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
main>section#categories ul>li:not(.opened):has(+ ul) {
|
||||
margin-bottom: 0;
|
||||
transition: margin 0.1s ease-out 0.25s;
|
||||
}
|
||||
|
||||
main>section#categories>ul>li.opened:has(+ ul) {
|
||||
margin-bottom: var(--gap, 5px);
|
||||
transition: margin 0.1s ease-in;
|
||||
}
|
||||
|
||||
main>section#categories ul>ul {
|
||||
--offset: 0rem;
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
|
||||
main>section#categories>ul ul>ul {
|
||||
margin-left: var(--gap, 5px);
|
||||
width: calc(100% - var(--gap, 5px));
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
@font-face {
|
||||
font-family: 'Chalet';
|
||||
src: url("/themes/default/fonts/chalet/chalet.otf") format('opentype');
|
||||
font-weight: 600;
|
||||
font-style: bold;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
@font-face {
|
||||
font-family: 'Cygre';
|
||||
src: url("/themes/default/fonts/cygre/Cygre-Bold.ttf");
|
||||
font-weight: 600;
|
||||
font-style: bold;
|
||||
}
|
|
@ -26,7 +26,7 @@ i.icon.arrow:not(.circle, .square)::after {
|
|||
bottom: 7px;
|
||||
}
|
||||
|
||||
i.icon.arrow:not(.circle, .square)::before {
|
||||
i.icon.arrow:not(.circle, .square, .short)::before {
|
||||
width: 16px;
|
||||
height: 2px;
|
||||
bottom: 10px;
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
i.icon.house {
|
||||
position: relative;
|
||||
margin-bottom: -7px;
|
||||
width: 18px;
|
||||
height: 14px;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid;
|
||||
border-top: 0;
|
||||
border-bottom: 0;
|
||||
border-top-right-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
background:
|
||||
linear-gradient(to left, currentColor 5px, transparent 0) no-repeat 0 bottom/4px 2px,
|
||||
linear-gradient(to left, currentColor 5px, transparent 0) no-repeat right bottom/4px 2px;
|
||||
}
|
||||
|
||||
i.icon.house::after,
|
||||
i.icon.house::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
i.icon.house::before {
|
||||
left: 0;
|
||||
top: -5px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 3px;
|
||||
transform: rotate(45deg);
|
||||
border-top: 2px solid;
|
||||
border-left: 2px solid;
|
||||
border-top-left-radius: 4px;
|
||||
}
|
||||
|
||||
i.icon.house::after {
|
||||
left: 3px;
|
||||
bottom: 0;
|
||||
width: 8px;
|
||||
height: 10px;
|
||||
border: 2px solid;
|
||||
border-radius: 100px;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom: 0;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
i.icon.user {
|
||||
width: 12px;
|
||||
height: 18px;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
i.icon.user::after,
|
||||
i.icon.user::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid;
|
||||
}
|
||||
|
||||
i.icon.user::before {
|
||||
left: 2px;
|
||||
top: 0;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
i.icon.user::after {
|
||||
top: 9px;
|
||||
width: 12px;
|
||||
height: 9px;
|
||||
border-bottom: 0;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
|
@ -1,15 +1,16 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
section[data-type="select"] {
|
||||
--width: max(14rem, 20vw);
|
||||
--height-element: 2rem;
|
||||
--height-close: var(--height-element);
|
||||
--height-open: max-content;
|
||||
div[data-type="select"] {
|
||||
--filter-width: max(14rem, 30vw);
|
||||
--filter-button-width: 2.5rem;
|
||||
--filter-height-element: var(--filters-height, 2rem);
|
||||
--filter-height-close: var(--filter-height-element);
|
||||
--filter-height-open: max-content;
|
||||
position: relative;
|
||||
width: var(--width);
|
||||
width: var(--filter-width);
|
||||
min-width: var(--width);
|
||||
height: var(--height-close);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
border-radius: 0.75rem;
|
||||
overflow-x: hidden;
|
||||
|
@ -17,18 +18,31 @@ section[data-type="select"] {
|
|||
transition: 0s;
|
||||
}
|
||||
|
||||
section[data-type="select"]>button:has(>i.icon.close) {
|
||||
align-self: end;
|
||||
height: 100%;
|
||||
div[data-type="select"]>section {
|
||||
position: relative;
|
||||
height: var(--filter-height-close);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
cursor: pointer;
|
||||
background-color: var(--tg-theme-button-color);
|
||||
transition: 0s;
|
||||
}
|
||||
|
||||
div[data-type="select"]>button:has(>i.icon.close) {
|
||||
z-index: 100;
|
||||
align-self: start;
|
||||
width: var(--filter-button-width);
|
||||
height: var(--filter-height-close);
|
||||
padding: 0 0.4rem;
|
||||
}
|
||||
|
||||
section[data-type="select"]:has(input[id$="title"]:checked)>button:has(>i.icon.close),
|
||||
section[data-type="select"]:focus>button:has(>i.icon.close) {
|
||||
div[data-type="select"]:has(>section>input[id$="title"]:checked)>button:has(>i.icon.close),
|
||||
div[data-type="select"]:focus>button:has(>i.icon.close) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
section[data-type="select"]:not(:focus, :has(>button>i.icon.close)):after {
|
||||
div[data-type="select"]:not(>section:focus, >section:has(>button>i.icon.close)):after {
|
||||
z-index: 30;
|
||||
content: '';
|
||||
top: calc(50% - 2.5px);
|
||||
|
@ -42,19 +56,19 @@ section[data-type="select"]:not(:focus, :has(>button>i.icon.close)):after {
|
|||
border-right: 5px solid transparent;
|
||||
}
|
||||
|
||||
section[data-type="select"]>input {
|
||||
div[data-type="select"]>section>input {
|
||||
left: -99999px;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
section[data-type="select"]>label {
|
||||
div[data-type="select"]>section>label {
|
||||
z-index: 10;
|
||||
order: 2;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: var(--height-element);
|
||||
width: calc(100% + var(--filter-button-width));
|
||||
height: var(--filter-height-element);
|
||||
padding: 0 1rem;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
|
@ -66,61 +80,62 @@ section[data-type="select"]>label {
|
|||
transition: 0s;
|
||||
}
|
||||
|
||||
section[data-type="select"]:is([data-select="open"], :focus)>input:not(:checked)+label[for$='title'] {
|
||||
div[data-type="select"]>section:is([data-select="open"], :focus)>input:not(:checked)+label[for$='title'] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
section[data-type="select"]:is([data-select="open"], :focus)>input:not(:checked)+label[for$='all']:not(:hover, :active, :focus) {
|
||||
div[data-type="select"]>section:is([data-select="open"], :focus)>input:not(:checked)+label[for$='all']:not(:hover, :active, :focus) {
|
||||
filter: brightness(90%);
|
||||
}
|
||||
|
||||
section[data-type="select"]>input:not(:checked)+label {
|
||||
div[data-type="select"]>section>input:not(:checked)+label {
|
||||
cursor: pointer;
|
||||
filter: brightness(80%);
|
||||
}
|
||||
|
||||
section[data-type="select"]:is([data-select="open"], :focus)>input+label:hover {
|
||||
div[data-type="select"]>section:is([data-select="open"], :focus)>input+label:hover {
|
||||
filter: brightness(110%);
|
||||
}
|
||||
|
||||
section[data-type="select"]:is([data-select="open"], :focus)>input:checked+label:hover {
|
||||
div[data-type="select"]>section:is([data-select="open"], :focus)>input:checked+label:hover {
|
||||
filter: brightness(120%);
|
||||
}
|
||||
|
||||
section[data-type="select"]:is([data-select="open"], :focus)>input+label:is(:active, :focus) {
|
||||
div[data-type="select"]>section:is([data-select="open"], :focus)>input+label:is(:active, :focus) {
|
||||
filter: brightness(60%);
|
||||
}
|
||||
|
||||
section[data-type="select"]:is([data-select="open"], :focus)>input:checked+label:is(:active, :focus) {
|
||||
div[data-type="select"]>section:is([data-select="open"], :focus)>input:checked+label:is(:active, :focus) {
|
||||
filter: brightness(70%);
|
||||
}
|
||||
|
||||
section[data-type="select"]>input:checked+label {
|
||||
div[data-type="select"]>section>input:checked+label {
|
||||
order: 1;
|
||||
max-width: calc(var(--width) - 2rem - 10px);
|
||||
max-width: calc(var(--filter-width) - 2rem - 10px);
|
||||
display: inline;
|
||||
line-height: var(--height-element);
|
||||
line-height: var(--filter-height-element);
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
section[data-type="select"]:is([data-select="open"], :focus)>input:checked+label {
|
||||
div[data-type="select"]>section:is([data-select="open"], :focus)>input:checked+label {
|
||||
max-width: initial;
|
||||
padding-right: initial;
|
||||
}
|
||||
|
||||
section[data-type="select"]:is([data-select="open"], :focus) {
|
||||
height: var(--height-open, max-content);
|
||||
div[data-type="select"]>section:is([data-select="open"], :focus) {
|
||||
height: var(--filter-height-open, max-content);
|
||||
}
|
||||
|
||||
section[data-type="select"]:is([data-select="open"], :focus)>label {
|
||||
div[data-type="select"]>section:is([data-select="open"], :focus)>label {
|
||||
position: relative;
|
||||
display: inline;
|
||||
line-height: var(--height-element);
|
||||
line-height: var(--filter-height-element);
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 500px) {
|
||||
section[data-type="select"]:only-child {
|
||||
div[data-type="select"]>section:only-child {
|
||||
--width: 100%
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@ a {
|
|||
|
||||
body {
|
||||
--gap: 16px;
|
||||
--width: calc(100% - var(--gap) * 2);
|
||||
/* --width: calc(100% - var(--gap) * 2); */
|
||||
--width: calc(100vw - var(--gap) * 2 - 1rem);
|
||||
--offset-x: 2%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -49,9 +50,10 @@ body:has(section#window) {
|
|||
aside {}
|
||||
|
||||
header {
|
||||
z-index: 1000;
|
||||
container-type: inline-size;
|
||||
container-name: header;
|
||||
margin-top: 2rem;
|
||||
position: relative;
|
||||
padding: 0 var(--offset-x);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -59,7 +61,12 @@ header {
|
|||
gap: 26px;
|
||||
}
|
||||
|
||||
body:has(>div#top>search)>header {
|
||||
margin-top: var(--header-margin-top, 2rem);
|
||||
}
|
||||
|
||||
main {
|
||||
--gap-main: calc(var(--gap) + 10px);
|
||||
container-type: inline-size;
|
||||
container-name: main;
|
||||
flex-grow: 1;
|
||||
|
@ -67,7 +74,7 @@ main {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: calc(var(--gap) + 10px);
|
||||
gap: var(--gap-main);
|
||||
transition: 0s;
|
||||
}
|
||||
|
||||
|
@ -101,7 +108,7 @@ main>article>h3 {
|
|||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
main>search {
|
||||
main>search.relative {
|
||||
--gap: 16px;
|
||||
--border-width: 1px;
|
||||
width: var(--width);
|
||||
|
@ -113,7 +120,71 @@ main>search {
|
|||
overflow: clip;
|
||||
}
|
||||
|
||||
footer {}
|
||||
div#top {
|
||||
z-index: 1000;
|
||||
position: fixed !important;
|
||||
top: 0;
|
||||
padding: 0.6rem 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
background-color: var(--tg-theme-bg-color);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
div#top>search.fixed {
|
||||
--gap: 16px;
|
||||
--border-width: 1px;
|
||||
width: var(--width);
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
border-radius: 1.375rem;
|
||||
backdrop-filter: contrast(0.8);
|
||||
overflow: hidden;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
a#cart {
|
||||
--size: 2.2rem;
|
||||
position: fixed !important;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
padding: 0.25rem 0.3rem 0.35rem 0.3rem;
|
||||
border-radius: 0.75rem;
|
||||
overflow: hidden;
|
||||
background-color: var(--tg-theme-section-header-text-color);
|
||||
box-shadow: 0px 0px 12px 3px rgba(0, 0, 0, 0.2);
|
||||
-webkit-box-shadow: 0px 0px 12px 3px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: 0px 0px 12px 3px rgba(0, 0, 0, 0.2);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
a#cart:before {
|
||||
content: var(--cart-amount);
|
||||
position: absolute;
|
||||
right: -0.5rem;
|
||||
top: -0.6rem;
|
||||
padding: 0.2rem 0.3rem;
|
||||
min-width: 0.7rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: small;
|
||||
border-radius: 1.125rem;
|
||||
border: 0.2rem solid var(--tg-theme-button-color);
|
||||
color: var(--tg-theme-button-color);
|
||||
background-color: var(--tg-theme-button-text-color);
|
||||
box-shadow: 0px 0px 12px 3px rgba(0, 0, 0, 0.2);
|
||||
-webkit-box-shadow: 0px 0px 12px 3px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: 0px 0px 12px 3px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-bottom: var(--footer-margin-bottom);
|
||||
}
|
||||
|
||||
footer>section#govno {
|
||||
align-items: center;
|
||||
|
@ -256,6 +327,7 @@ input {
|
|||
.cost.currency:after {
|
||||
content: var(--currency);
|
||||
margin-left: var(--currency-offset, 0.1rem);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.cost.plus:before {
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
header>nav#menu {
|
||||
nav#menu.relative {
|
||||
container-type: inline-size;
|
||||
container-name: menu;
|
||||
margin-bottom: 1rem;
|
||||
width: var(--width);
|
||||
min-height: 3rem;
|
||||
display: flex;
|
||||
|
@ -13,7 +12,7 @@ header>nav#menu {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
header>nav#menu>a[type="button"] {
|
||||
nav#menu.relative>a[type="button"] {
|
||||
height: 3rem;
|
||||
padding: unset;
|
||||
border-radius: 1.375rem;
|
||||
|
@ -21,34 +20,76 @@ header>nav#menu>a[type="button"] {
|
|||
background-color: var(--tg-theme-button-color);
|
||||
}
|
||||
|
||||
header>nav#menu>a[type=button]>:first-child {
|
||||
nav#menu.relative>a[type=button]>:first-child {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
header>nav#menu>a[type="button"]>* {
|
||||
nav#menu.relative>a[type="button"]>* {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
@container header (max-width: 450px) {
|
||||
header>nav#menu>a[type="button"]:nth-child(1)>i.icon+span {
|
||||
nav#menu.relative>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 {
|
||||
nav#menu.relative>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 {
|
||||
nav#menu.relative>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 {
|
||||
nav#menu.relative>a[type="button"]>i.icon+span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
nav#menu.fixed {
|
||||
z-index: 1000;
|
||||
position: fixed !important;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: var(--menu-height, 3rem);
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
background-color: var(--tg-theme-secondary-bg-color);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
nav#menu.fixed>a[type="button"] {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
padding: 0.6rem;
|
||||
color: var(--tg-theme-text-color);
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
nav#menu.fixed>a[type="button"]>i.icon {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
nav#menu.fixed>a[type="button"].cart:before {
|
||||
content: var(--cart-amount);
|
||||
position: absolute;
|
||||
margin-right: -1.3rem;
|
||||
margin-top: -1.5rem;
|
||||
padding: 0.2rem 0.3rem;
|
||||
min-width: 0.7rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: small;
|
||||
color: var(--tg-color-scheme);
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ section#window>div.card>div.images {
|
|||
transition: 0s;
|
||||
}
|
||||
|
||||
section#window>div.card>div.images:has(> img:last-child:nth-child(2)) {
|
||||
section#window>div.card>div.images:not(.extend):has(> img:last-child:nth-child(2)) {
|
||||
padding: 0rem 1rem;
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,4 +1,6 @@
|
|||
*
|
||||
!acquirings/
|
||||
!acquirings/robokassa
|
||||
!deliveries/
|
||||
!.gitignore
|
||||
!*.sample
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
// Exit (success)
|
||||
return [
|
||||
'login' => '',
|
||||
'passwords' => ['', '']
|
||||
];
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
// Exit (success)
|
||||
return [
|
||||
'login' => '',
|
||||
'passwords' => ['', '']
|
||||
];
|
|
@ -13,5 +13,59 @@ return [
|
|||
'offer' => false,
|
||||
'sim' => null,
|
||||
'mail' => null
|
||||
],
|
||||
'search' => [
|
||||
'enabled' => true,
|
||||
'position' => 'fixed'
|
||||
],
|
||||
'cart' => [
|
||||
'enabled' => false
|
||||
],
|
||||
'css' => [
|
||||
'catalog-button-cart-background' => '#40a7e3',
|
||||
'product-button-cart-background' => '#40a7e3',
|
||||
'catalog-button-cart-added-background' => '#90be36',
|
||||
'product-button-cart-added-background' => '#90be36'
|
||||
],
|
||||
'account' => [
|
||||
'enabled' => false
|
||||
],
|
||||
'menu' => [
|
||||
'enabled' => true,
|
||||
'position' => 'fixed'
|
||||
],
|
||||
'header' => [
|
||||
'enabled' => true,
|
||||
'position' => 'fixed'
|
||||
],
|
||||
'acquirings' => [
|
||||
'robokassa' => [
|
||||
'enabled' => true,
|
||||
'mode' => 'test'
|
||||
]
|
||||
],
|
||||
'input' => [
|
||||
'deliveries' => [
|
||||
'site' => [],
|
||||
'chat' => [
|
||||
'sim',
|
||||
'name',
|
||||
'destination',
|
||||
'address',
|
||||
'commentary'
|
||||
]
|
||||
]
|
||||
],
|
||||
'deliveries' => [
|
||||
'cdek' => [
|
||||
'enabled' => true,
|
||||
'label' => 'CDEK'
|
||||
]
|
||||
],
|
||||
'chats' => [
|
||||
[
|
||||
'id' => -1002599391893,
|
||||
'orders' => true
|
||||
]
|
||||
]
|
||||
];
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
!*.mp4
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
|
@ -8,6 +8,7 @@ namespace mirzaev\huesos\views;
|
|||
use mirzaev\huesos\models\session,
|
||||
mirzaev\huesos\models\account,
|
||||
mirzaev\huesos\models\settings,
|
||||
mirzaev\huesos\models\cart,
|
||||
mirzaev\huesos\models\enumerations\language,
|
||||
mirzaev\huesos\models\enumerations\currency;
|
||||
|
||||
|
@ -139,7 +140,7 @@ final class templater extends controller implements array_access
|
|||
'sim_format',
|
||||
function (string|int|float|null $sim = null) {
|
||||
// Universalizing SIM-number
|
||||
preg_match_all('/\d/', $sim, $numbers);
|
||||
preg_match_all('/\d/', (string) $sim, $numbers);
|
||||
$universalized = implode($numbers[0]);
|
||||
|
||||
// Deinitializing unnecessary variables(
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% if settings.account.enabled and not (settings.menu.enabled and settings.menu.position == 'fixed') %}
|
||||
{% if account is empty %}
|
||||
<section id="account">
|
||||
<!-- <a onclick="core.account.authentication()">
|
||||
|
@ -16,6 +17,7 @@
|
|||
</a>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
{% extends "/themes/default/core.html" %}
|
||||
|
||||
{% block body %}
|
||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/acquirings/robokassa/fail.css" />
|
||||
|
||||
<main>
|
||||
<h2 id="title" class="unselectable">{{ h2 }}</h2>
|
||||
{% if test %}
|
||||
<small>{{ test }}</small>
|
||||
{% endif %}
|
||||
<span id="closing" style="--iterator: '{{ closing.iterator }}';"><i class="icon loading spinner animated"></i>{{
|
||||
closing.title }}</span>
|
||||
</main>
|
||||
|
||||
<script src="/js/closing.js"></script>
|
||||
{% endblock %}
|
|
@ -0,0 +1,16 @@
|
|||
{% extends "/themes/default/core.html" %}
|
||||
|
||||
{% block body %}
|
||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/acquirings/robokassa/success.css" />
|
||||
|
||||
<main>
|
||||
<h2 id="title" class="unselectable">{{ h2 }}</h2>
|
||||
{% if test %}
|
||||
<small>{{ test }}</small>
|
||||
{% endif %}
|
||||
<span id="closing" style="--iterator: '{{ closing.iterator }}';"><i class="icon loading spinner animated"></i>{{
|
||||
closing.title }}</span>
|
||||
</main>
|
||||
|
||||
<script src="/js/closing.js"></script>
|
||||
{% endblock %}
|
|
@ -16,7 +16,7 @@
|
|||
},
|
||||
root: 'map',
|
||||
apiKey: 'a222c5fe-fee7-420a-b979-aec0473a0400',
|
||||
servicePath: 'https://arming.dev.mirzaev.sexy/api/cdek',
|
||||
servicePath: 'https://telegram.arming.ru/api/cdek',
|
||||
hideFilters: {
|
||||
have_cashless: true,
|
||||
have_cash: true,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% if cart.products is not empty %}
|
||||
<button id="order" class="rounded merged column unselectable" onclick="core.cart.share(this)" {% if
|
||||
session.buffer.delivery[account.buffer.delivery.company ?? session.buffer.delivery.company].ready==false %}
|
||||
session.buffer.delivery[account.buffer.delivery.company ?? session.buffer.delivery.company].ready==false and 'address' in settings.input.deliveries.site %}
|
||||
disabled="true" {% endif %}>Оформить заказ</button>
|
||||
{% endif %}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
class="rounded column unselectable">
|
||||
<div class="row">
|
||||
<span id="amount">{{ cart.summary.amount ?? 0 }}</span>
|
||||
<span>{{ language.name == "ru" ? "товаров за " : "products worth" }}</span>
|
||||
<!-- <span>{{ language.name == "ru" ? "товаров за " : "products worth" }}</span> -->
|
||||
<span>{{ language.name == "ru" ? "товаров на " : "products worth" }}</span>
|
||||
<span id="cost" class="cost currency">{{ cart.summary.cost ?? 0 }}</span>
|
||||
{% if session.buffer.delivery[account.buffer.delivery.company ?? session.buffer.delivery.company].cost %}
|
||||
<span id="cost_delivery" class="cost delivery currency plus hint">{{ session.buffer.delivery[account.buffer.delivery.company ?? session.buffer.delivery.company].cost }}</span>
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<a id="cart" class="animated opacity" type="button" href="/cart">
|
||||
<i class="icon shopping cart"></i>
|
||||
</a>
|
|
@ -1,16 +1,70 @@
|
|||
{% if categories is not empty %}
|
||||
{% if settings.catalog.categories.structure == 'pages' %}
|
||||
<section id="categories" class="unselectable">
|
||||
{% for category in categories %}
|
||||
<a id="category_{{ category.identifier }}" data-category-identifier="{{ category.identifier }}" class="category"
|
||||
<a id="category_{{ category.identifier }}" data-category-identifier="{{ category.identifier }}"
|
||||
class="category{% if settings.catalog.categories.display == 'column' %} column{% endif %}"
|
||||
type="button" href="?category={{ category.identifier }}"
|
||||
onclick="core.catalog.parameters.set('category', {{ category.identifier }}); return !core.catalog.search(event);"
|
||||
onkeydown="event.keyCode === 13 && (core.catalog.parameters.set('category', {{ category.identifier }}), core.catalog.search(event))"
|
||||
tabindex="3">
|
||||
tabindex="3"
|
||||
>
|
||||
{% if category.images %}
|
||||
<img src="{{ category.images.0.storage.400 }}" alt="{{ category.name }}" ondragstart="return false;">
|
||||
{% if settings.catalog.categories.display == 'column' and settings.catalog.categories.buttons.separator.enabled %}
|
||||
<div class="separator gradient"></div>
|
||||
{% endif %}
|
||||
<p>{{ category.name }}</p>
|
||||
<img src="{{ category.images.0.storage.400 }}"
|
||||
class="{% if settings.catalog.categories.display == 'column' %}right{% endif %}" alt="{{ category.name }}" ondragstart="return false;">
|
||||
{% endif %}
|
||||
<p class="{% if settings.catalog.categories.buttons.texts.background %}background{% endif %}">{{ category.name }}{% if category.description %} <small>{{ category.description }}</small>{% endif %}</p>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% elseif settings.catalog.categories.structure == 'lists' %}
|
||||
{% macro generate(structure, categories) %}
|
||||
{% for category, descendants in structure %}
|
||||
{% if descendants is empty %}
|
||||
<li id="category_{{ categories[category].identifier }}" type="button"
|
||||
onclick="core.catalog.parameters.set('category', {{ categories[category].identifier }}); return !core.catalog.search(event);">
|
||||
{{ categories[category].name }}</li>
|
||||
{% else %}
|
||||
<li id="category_{{ categories[category].identifier }}" type="button" class="openable" onclick="this.classList.toggle('opened'); for (const sibling of [...this.parentElement.children].filter(children => children !== this)) { sibling.classList.remove('opened'); }">
|
||||
{{ categories[category].name }}
|
||||
{% if settings.catalog.categories.buttons.lists.arrow %}
|
||||
<i class="icon arrow short"></i>
|
||||
{% endif %}
|
||||
</li>
|
||||
<ul>
|
||||
<li id="category_{{ categories[category].identifier }}" type="button"
|
||||
onclick="core.catalog.parameters.set('category', {{ categories[category].identifier }}); return !core.catalog.search(event);">
|
||||
Все товары
|
||||
</li>
|
||||
{{ _self.generate(descendants, categories) }}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
<section id="categories" class="unselectable">
|
||||
{% if structure is not empty %}
|
||||
<ul>
|
||||
{% for category, descendants in structure %}
|
||||
<li id="category_{{ categories[category].identifier }}" class="category openable" type="button" tabindex="3" onclick="this.classList.toggle('opened'); for (const sibling of [...this.parentElement.children].filter(children => children !== this)) { sibling.classList.remove('opened'); }">
|
||||
{% if categories[category].images %}
|
||||
{% if settings.catalog.categories.display == 'column' and settings.catalog.categories.buttons.separator.enabled %}
|
||||
<div class="separator gradient"></div>
|
||||
{% endif %}
|
||||
<img src="{{ categories[category].images.0.storage.400 }}"
|
||||
class="{% if settings.catalog.categories.display == 'column' %}right{% endif %}" alt="{{ categories[category].name }}" ondragstart="return false;">
|
||||
{% endif %}
|
||||
<p class="{% if settings.catalog.categories.buttons.texts.background %}background{% endif %}">{{ categories[category].name }}{% if category.description %} <small>{{ categories[category].description }}</small>{% endif %}</p>
|
||||
</li>
|
||||
<ul>
|
||||
{{ _self.generate(descendants, categories) }}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{% if filters is not empty %}
|
||||
<section id="filters" class="unselectble">
|
||||
<section class="unselectable" data-type="select" tabindex="4">
|
||||
<div data-type="select">
|
||||
<section class="unselectable" tabindex="4">
|
||||
{% set buffer_brand = session.buffer.catalog.filters.brand ?? account.buffer.catalog.filters.brand %}
|
||||
<input name="brand" type="radio" id="brand_title" {% if buffer_brand is empty %}checked{% endif %}>
|
||||
<label for="brand_title" type="button">{{ language.name == 'ru' ? 'Бренд' : 'Brand' }}</label>
|
||||
|
@ -16,8 +17,10 @@
|
|||
{{ brand }}
|
||||
</label>
|
||||
{% endfor %}
|
||||
<button onclick="core.catalog.parameters.set('brand', null); core.catalog.search(event)"><i
|
||||
class="icon small close"></i></button>
|
||||
</section>
|
||||
<button type="button" onclick="core.catalog.parameters.set('brand', null); core.catalog.search(event)">
|
||||
<i class="icon small close"></i>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
{% macro generate(categories, registry) %}
|
||||
{% for category, descendants in categories %}
|
||||
{% if descendants is empty %}
|
||||
<li id="{{ registry[category].getId() }}">{{ registry[category].name }}</li>
|
||||
{% else %}
|
||||
<li id="{{ registry[category].getId() }}">{{ registry[category].name }}</li>
|
||||
<ul>
|
||||
{{ _self.generate(descendants, registry) }}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
{% if registry is not empty and structure is not empty %}
|
||||
<ul>
|
||||
{% for category, descendants in structure %}
|
||||
<li id="{{ registry[category].getId() }}">{{ registry[category].name }}</li>
|
||||
<ul>
|
||||
{{ _self.generate(descendants, registry) }}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
|
@ -0,0 +1,23 @@
|
|||
{% if filters is not empty %}
|
||||
<section id="filters" class="unselectble">
|
||||
<section class="unselectable" data-type="select" tabindex="4">
|
||||
{% set buffer_brand = session.buffer.catalog.filters.brand ?? account.buffer.catalog.filters.brand %}
|
||||
<input name="brand" type="radio" id="brand_title" {% if buffer_brand is empty %}checked{% endif %}>
|
||||
<label for="brand_title" type="button">{{ language.name == 'ru' ? 'Бренд' : 'Brand' }}</label>
|
||||
<!-- <input name="brand" type="radio" id="brand_all" oninput="core.catalog.parameters.set('brand', null); core.catalog.search(event)">
|
||||
<label for="brand_all" type="button">
|
||||
{{ language.name == 'ru' ? 'Все бренды' : 'All brands' }}
|
||||
</label> -->
|
||||
{% for brand in filters.brands %}
|
||||
<input name="brand" type="radio" id="brand_{{ loop.index }}"
|
||||
oninput="core.catalog.parameters.set('brand', '{{ brand }}'); core.catalog.search(event)" {% if
|
||||
brand==buffer_brand %}checked{% endif %}>
|
||||
<label for="brand_{{ loop.index }}" type="button">
|
||||
{{ brand }}
|
||||
</label>
|
||||
{% endfor %}
|
||||
<button onclick="core.catalog.parameters.set('brand', null); core.catalog.search(event)"><i
|
||||
class="icon small close"></i></button>
|
||||
</section>
|
||||
</section>
|
||||
{% endif %}
|
|
@ -1,7 +1,21 @@
|
|||
<search id="search">
|
||||
{% if settings.search.position == 'fixed' %}
|
||||
<div id="top">
|
||||
<search id="search" class="fixed">
|
||||
<label class="unselectable"><i class="icon search"></i></label>
|
||||
{% set buffer_search = session.buffer.catalog.search.text ?? account.buffer.catalog.search.text %}
|
||||
<input placeholder="{{ language.name == 'ru' ? 'Поиск по каталогу' : 'Search in the catalog' }}" type="search" tabindex="1"
|
||||
<input placeholder="{{ language.name == 'ru' ? 'Поиск по каталогу' : 'Search in the catalog' }}" type="search"
|
||||
tabindex="1"
|
||||
onkeyup="event.keyCode === 9 || core.catalog.parameters.set('text', this.value); return core.catalog.search(event, this)"
|
||||
{% if buffer_search is not empty %} value="{{ buffer_search }}" {% endif %} />
|
||||
</search>
|
||||
</div>
|
||||
{% elseif settings.search.position == 'relative' %}
|
||||
<search id="search" class="relative">
|
||||
<label class="unselectable"><i class="icon search"></i></label>
|
||||
{% set buffer_search = session.buffer.catalog.search.text ?? account.buffer.catalog.search.text %}
|
||||
<input placeholder="{{ language.name == 'ru' ? 'Поиск по каталогу' : 'Search in the catalog' }}" type="search"
|
||||
tabindex="1"
|
||||
onkeyup="event.keyCode === 9 || core.catalog.parameters.set('text', this.value); return core.catalog.search(event, this)"
|
||||
{% if buffer_search is not empty %} value="{{ buffer_search }}" {% endif %} />
|
||||
</search>
|
||||
{% endif %}
|
||||
|
|
|
@ -3,6 +3,19 @@
|
|||
{% block css %}
|
||||
{{ parent() }}
|
||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/catalog.css" />
|
||||
{% if settings.catalog.categories.display == 'column' and settings.catalog.categories.structure == 'lists' %}
|
||||
{% if settings.catalog.categories.buttons.lists.separated %}
|
||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/catalog/lists/separated.css" />
|
||||
{% endif %}
|
||||
|
||||
{% if settings.catalog.categories.buttons.lists.blocks %}
|
||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/catalog/lists/blocks.css" />
|
||||
{% endif %}
|
||||
|
||||
{% if settings.catalog.categories.buttons.lists.arrow %}
|
||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/arrow.css" />
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<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/shopping_cart.css" />
|
||||
|
@ -11,12 +24,23 @@
|
|||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/minus.css" />
|
||||
{% endblock %}
|
||||
|
||||
{% block search %}
|
||||
{% if settings.search.enabled and settings.search.position == 'fixed' %}
|
||||
{% include "/themes/default/catalog/elements/search.html" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<h2 id="title" class="unselectable">{{ h2 }}</h2>
|
||||
{% if settings.search.enabled and settings.search.position == 'relative' %}
|
||||
{% include "/themes/default/catalog/elements/search.html" %}
|
||||
{% endif %}
|
||||
{% include "/themes/default/catalog/elements/categories.html" %}
|
||||
{% include "/themes/default/catalog/elements/filters.html" %}
|
||||
{% include "/themes/default/catalog/elements/products.html" %}
|
||||
{% if settings.cart.enabled %}
|
||||
{% include "/themes/default/catalog/elements/cart.html" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
<script>
|
||||
const search = document.getElementById('search');
|
||||
const search_next = search.nextElementSibling
|
||||
let search_to_fixed, search_to_normal;
|
||||
|
||||
setInterval(function () {
|
||||
if (search_next.getBoundingClientRect()?.top < window.scrollY) {
|
||||
clearTimeout(search_to_normal);
|
||||
|
||||
if (search_to_fixed == null) {
|
||||
search_to_fixed = setTimeout(function () {
|
||||
let search_wrap = document.getElementById('wrap_search');
|
||||
|
||||
if (search_wrap instanceof HTMLElement) {
|
||||
if (search.parentElement !== search_wrap) {
|
||||
search_wrap.appendChild(search);
|
||||
}
|
||||
} else {
|
||||
const search_wrap = document.createElement('div');
|
||||
search_wrap.setAttribute('id', 'wrap_search');
|
||||
search_wrap.classList.add('animated', 'slide', 'down');
|
||||
|
||||
|
||||
search_wrap.appendChild(search);
|
||||
document.body.appendChild(search_wrap);
|
||||
}
|
||||
|
||||
search_to_fixed = null;
|
||||
search_to_normal = null;
|
||||
}, 200);
|
||||
}
|
||||
|
||||
} else {
|
||||
clearTimeout(search_to_fixed);
|
||||
|
||||
if (search_to_normal == null) {
|
||||
search_to_normal = setTimeout(function () {
|
||||
let search_wrap = document.getElementById('wrap_search');
|
||||
|
||||
if (search_wrap instanceof HTMLElement) {
|
||||
if (core.main instanceof HTMLElement) {
|
||||
// Found the <main> element
|
||||
|
||||
// Initializing the title <h2> element
|
||||
const title = document.getElementById("title");
|
||||
|
||||
if (title instanceof HTMLElement) {
|
||||
// Initialized the title <h2> elemment in the <main> element
|
||||
|
||||
// Writing the search <search> element after the title <h2> element in the <main> element
|
||||
core.main.insertBefore(
|
||||
search,
|
||||
title.nextElementSibling,
|
||||
);
|
||||
} else {
|
||||
// Not initialized the title <h2> element in the <main> element
|
||||
|
||||
// Inititalize the first element in the <main> element
|
||||
const first = core.main.firstElementChild;
|
||||
|
||||
if (first instanceof HTMLElement) {
|
||||
// Initialized the first element in the <main> element
|
||||
|
||||
// Writing the search <search> element before the first element in the <main> element
|
||||
core.main.insertBefore(
|
||||
search,
|
||||
first,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
search_wrap?.remove();
|
||||
}
|
||||
|
||||
search_to_fixed = null;
|
||||
search_to_normal = null;
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
</script>
|
||||
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
{% block meta %}
|
||||
<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, height=device-height, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no, viewport-fit=cover"> -->
|
||||
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0, viewport-fit=cover">
|
||||
{% for meta in head.metas %}
|
||||
<meta {% for name, value in meta.attributes %}{{ name }}="{{ value }}" {% endfor %}>
|
||||
{% endfor %}
|
||||
|
@ -17,11 +18,95 @@
|
|||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/window.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/fonts/dejavu.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/fonts/kabrio.css" rel="preload" />
|
||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/fonts/cygre.css" rel="preload" />
|
||||
<!-- <link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/fonts/chalet.css" rel="preload" /> -->
|
||||
<style>
|
||||
:root {
|
||||
--currency: "{{ currency.symbol ?? '$' }}";
|
||||
--days: "{{ language.name == 'ru' ? 'дней' : 'days' }}";
|
||||
--days-short: "{{ language.name == 'ru' ? 'дн' : 'd' }}";
|
||||
|
||||
{% if settings.search.enabled and settings.search.position == 'fixed' %}
|
||||
{% if settings.menu.enabled %}
|
||||
{% if settings.menu.position == 'fixed' %}
|
||||
--header-margin-top: 3rem;
|
||||
{% elseif settings.menu.position == 'relative' %}
|
||||
--header-margin-top: 5rem;
|
||||
{% endif %}
|
||||
{% else %}
|
||||
--header-margin-top: 3rem;
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if settings.menu.enabled and settings.menu.position == 'fixed' %}
|
||||
--menu-height: {{ settings.menu.height ?? '3rem' }};
|
||||
--footer-margin-bottom: var(--menu-height);
|
||||
{% endif %}
|
||||
|
||||
{% for parameter, value in settings.css %}
|
||||
--{{ parameter }}: {{ value }};
|
||||
{% endfor %}
|
||||
|
||||
{% if cart.summary.amount > 0 %}
|
||||
--cart-amount: "{{ cart.summary.amount }}";
|
||||
{% endif %}
|
||||
|
||||
{% if settings.catalog.categories.buttons.height %}
|
||||
--catalog-categories-buttons-height: {{ settings.catalog.categories.buttons.height }};
|
||||
{% endif %}
|
||||
|
||||
{% if settings.catalog.categories.buttons.background %}
|
||||
--catalog-categories-buttons-background: {{ settings.catalog.categories.buttons.background }};
|
||||
{% endif %}
|
||||
|
||||
{% if settings.catalog.categories.buttons.texts.title.color %}
|
||||
--catalog-categories-buttons-texts-title-color: {{ settings.catalog.categories.buttons.texts.title.color }};
|
||||
{% endif %}
|
||||
|
||||
{% if settings.catalog.categories.buttons.texts.description.color %}
|
||||
--catalog-categories-buttons-texts-description-color: {{ settings.catalog.categories.buttons.texts.description.color }};
|
||||
{% endif %}
|
||||
|
||||
{% if settings.catalog.categories.buttons.images.filter %}
|
||||
--catalog-categories-buttons-images-filter: {{ settings.catalog.categories.buttons.images.filter }};
|
||||
{% endif %}
|
||||
|
||||
{% if settings.catalog.categories.display == 'column' %}
|
||||
--catalog-categories-wrap-flex-wrap: unset;
|
||||
--catalog-categories-wrap-flex-direction: 'column';
|
||||
--catalog-categories-buttons-width: 100%;
|
||||
|
||||
{% if settings.catalog.categories.buttons.separator.width %}
|
||||
--catalog-categories-buttons-separator-width: {{ settings.catalog.categories.buttons.separator.width }};
|
||||
{% endif %}
|
||||
|
||||
{% if settings.catalog.categories.buttons.lists.background %}
|
||||
--catalog-categories-buttons-lists-background-color: {{ settings.catalog.categories.buttons.lists.background }};
|
||||
{% endif %}
|
||||
|
||||
{% if settings.catalog.categories.buttons.lists.separator %}
|
||||
--catalog-categories-buttons-lists-separator-color: {{ settings.catalog.categories.buttons.lists.separator }};
|
||||
{% endif %}
|
||||
|
||||
{% if settings.catalog.categories.buttons.lists.height %}
|
||||
--catalog-categories-buttons-lists-height: {{ settings.catalog.categories.buttons.lists.height }};
|
||||
{% endif %}
|
||||
|
||||
{% if settings.catalog.categories.buttons.texts.position.vertical == 'top' %}
|
||||
--catalog-categories-buttons-texts-bottom: auto;
|
||||
{% elseif settings.catalog.categories.buttons.texts.position.vertical == 'center' %}
|
||||
--catalog-categories-buttons-texts-top: auto;
|
||||
--catalog-categories-buttons-texts-bottom: auto;
|
||||
{% elseif settings.catalog.categories.buttons.texts.position.vertical == 'bottom' %}
|
||||
--catalog-categories-buttons-texts-top: auto;
|
||||
{% endif %}
|
||||
|
||||
{% if settings.catalog.categories.buttons.texts.width %}
|
||||
--catalog-categories-buttons-texts-width: {{ settings.catalog.categories.buttons.texts.width }};
|
||||
{% endif %}
|
||||
{% else %}
|
||||
--catalog-categories-buttons-texts-top: auto;
|
||||
{% endif %}
|
||||
}
|
||||
</style>
|
||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/animations.css" />
|
||||
|
|
|
@ -1,15 +1,24 @@
|
|||
{% use "/themes/default/menu.html" with css as menu_css, body as menu_body, js as menu_js %}
|
||||
|
||||
{% block css %}
|
||||
{% if settings.menu.enabled %}
|
||||
{{ block('menu_css') }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<header>
|
||||
<header class="{{ settings.header.position }}">
|
||||
{% if settings.menu.enabled and settings.menu.position == 'relative' %}
|
||||
{{ block('menu_body') }}
|
||||
{% endif %}
|
||||
</header>
|
||||
{% if settings.menu.enabled and settings.menu.position == 'fixed' %}
|
||||
{{ block('menu_body') }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{% if settings.menu.enabled %}
|
||||
{{ block('menu_js') }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -16,12 +16,20 @@
|
|||
{% block body %}
|
||||
<!-- {{ block('connection_body') }} -->
|
||||
{{ block('account_body') }}
|
||||
|
||||
{% if settings.header.enabled %}
|
||||
{{ block('header') }}
|
||||
{% endif %}
|
||||
|
||||
{% block search %}
|
||||
{% endblock %}
|
||||
|
||||
<main>
|
||||
{% block main %}
|
||||
{{ main|raw }}
|
||||
{% endblock %}
|
||||
</main>
|
||||
|
||||
{{ block('footer') }}
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -8,11 +8,29 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<nav id="menu">
|
||||
{% if settings.menu.position == 'fixed' %}
|
||||
|
||||
<nav id="menu" class="fixed">
|
||||
{% for button in menu %}
|
||||
<a href='{{ button.urn }}' onclick="return core.loader.load('{{ button.urn }}');" type="button" class="unselectable"
|
||||
title="{{ button.name }}" {% if button.style %}
|
||||
style="{% for parameter, value in button.style %}{{ parameter ~ ': ' ~ value ~ '; ' }}{% endfor %}" {% endif %}>
|
||||
<a{% if button.identifier %} id="{{ button.identifier }}" {% endif %} href='{{ button.urn }}' onclick="return core.loader.load('{{ button.urn }}');" type="button"
|
||||
class="unselectable{% if button.class %} {{ button.class }}{% endif %}" title="{{ button.name }}" {% if button.style
|
||||
%} style="{% for parameter, value in button.style %}{{ parameter ~ ': ' ~ value ~ '; ' }}{% endfor %}" {% endif %}>
|
||||
{% if button.icon %}
|
||||
<i class="icon {{ button.icon.class }}" {% if button.icon.style %}
|
||||
style="{% for parameter, value in button.icon.style %}{{ parameter ~ ': ' ~ value ~ '; ' }}{% endfor %}" {% endif
|
||||
%}></i>
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
|
||||
{% elseif settings.menu.position == 'relative' %}
|
||||
|
||||
<nav id="menu" class="relative">
|
||||
{% for button in menu %}
|
||||
<a{% if button.identifier %} id="{{ button.identifier }}" {% endif %}href='{{ button.urn }}' onclick="return core.loader.load('{{ button.urn }}');" type="button"
|
||||
class="unselectable{% if button.class %} {{ button.class }}{% endif %}" title="{{ button.name }}" {% if button.style
|
||||
%} style="{% for parameter, value in button.style %}{{ parameter ~ ': ' ~ value ~ '; ' }}{% endfor %}" {% endif %}>
|
||||
{% if button.icon %}
|
||||
<i class="icon {{ button.icon.class }}" {% if button.icon.style %}
|
||||
style="{% for parameter, value in button.icon.style %}{{ parameter ~ ': ' ~ value ~ '; ' }}{% endfor %}" {% endif
|
||||
|
@ -25,6 +43,8 @@
|
|||
</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
{{ parent() }}
|
||||
<style>
|
||||
main>article#offer {
|
||||
margin-bottom: var(--footer-margin-bottom);
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue