superpacks models and pages, short calculator, main page texts

This commit is contained in:
2026-04-26 13:19:43 +05:00
parent aa9b2e722c
commit 0d48c55530
33 changed files with 1873 additions and 322 deletions

View File

@@ -250,6 +250,7 @@ final class index extends core
$page = $this->view->render(
'pages/index.html',
[
'uri' => 'https://' . DOMAIN,
'smartphone' => $this->request->smartphone,
'tablet' => $this->request->tablet
]

View File

@@ -48,6 +48,7 @@ final class offer extends core
$page = $this->view->render(
'pages/offer.html',
[
'uri' => 'https://' . DOMAIN . '/offer',
'smartphone' => $this->request->smartphone,
'tablet' => $this->request->tablet
]

View File

@@ -6,10 +6,11 @@ namespace kodorvan\site\controllers;
// Files of the project
use kodorvan\site\controllers\core,
kodorvan\site\models\superpack;
kodorvan\site\models\superpack as model;
// Framework for PHP
use mirzaev\minimal\http\enumerations\content,
mirzaev\minimal\http\enumerations\method,
mirzaev\minimal\http\enumerations\status;
// Baza database
@@ -53,17 +54,18 @@ final class superpack extends core
// Request for HTML response
// Initializing the superpack
$superpack = new superpack()->read(filter: fn(record $record) => $record->urn === $urn && $record->active === 1);
$superpack = new model()->read(filter: fn(record $record) => $record->urn === $urn && $record->active === 1);
if ($superpack instanceof superpack) {
if ($superpack instanceof model) {
// Initialized the superpack
// Render page
$page = $this->view->render(
'pages/article.html',
[
'uri' => 'https://' . DOMAIN . "/superpack/$urn",
'article' => [
'urn' => $urn,
'urn' => $superpack->urn,
'title' => $superpack->title,
'html' => $superpack->html
],
@@ -95,4 +97,88 @@ final class superpack extends core
// Exit (fail)
return null;
}
/**
* Page: superpack
*
* @return null
*/
public function create(
?string $identifier = null,
?string $urn = null,
?string $title = null,
?string $html = null,
?string $text = null,
string|int|float|null $supercost = null
): null {
if ($this->request->method === method::get) {
// GET
if (str_contains($this->request->headers['accept'] ?? '', content::html->value)) {
// Request for HTML response
// Render page
$page = $this->view->render(
'pages/system/superpack/create.html',
[
'uri' => 'https://' . DOMAIN . "/system/superpack/create",
'smartphone' => $this->request->smartphone,
'tablet' => $this->request->tablet
]
);
// Sending response
$this->response
->start()
->clean()
->sse()
->write($page)
->validate($this->request)
?->body()
->end();
// Deinitializing rendered page
unset($page);
// Exit (success)
return null;
}
} else if ($this->request->method === method::put) {
// PUT
// Initializing the superpack
$superpack = new model()->read(filter: fn(record $record) => $record->urn === $urn);
if ($superpack instanceof model) {
// The superpack is already created
} else {
// The superpack is not already created
// Sanitizing
$urn = preg_replace('/[^\w\d\-.]+/', '', $urn);
$title = preg_replace('/[^\w\d\s\-.,!]+/u', '', $title);
$supercost = (float) preg_replace('/[^\d.]+/', '', $supercost);
// Creating the superpack
$superpack = new model()->write(
urn: $urn,
title: $title,
html: $html,
text: $text,
supercost: $supercost
);
if ($superpack instanceof record) {
// Created the superpack
// Sending redirect to the superpack
header('Location: /superpack/' . $urn);
}
}
}
// Exit (fail)
return null;
}
}

View File

@@ -74,11 +74,11 @@ final class superpack extends core implements record_interface
->columns(
new column('identifier', type::long_long_unsigned),
/* new column('account', type::long_long_unsigned), */
new column('urn', type::long_long_unsigned),
new column('title', type::string, ['length' => 64]),
new column('urn', type::string, ['length' => 64]),
new column('title', type::string, ['length' => 128]),
new column('html', type::string, ['length' => 8192]),
new column('text', type::string, ['length' => 8192]),
new column('supercost', type::integer_unsigned),
new column('supercost', type::float),
new column('active', type::char),
new column('updated', type::integer_unsigned),
new column('created', type::integer_unsigned)
@@ -99,7 +99,7 @@ final class superpack extends core implements record_interface
* @param string $title Title
* @param string|null $html Content (HTML)
* @param string|null $html Content (text)
* @param int $supercost Cost
* @param int|float|null $supercost Cost
* @param int $active Is the record active?
*
* @return record|false The record, if created
@@ -110,7 +110,7 @@ final class superpack extends core implements record_interface
string $title,
?string $html = null,
?string $text = null,
?int $supercost = null,
int|float|null $supercost = null,
bool $active = true,
): record|false {
if (!empty($html) || !empty($text)) {
@@ -122,7 +122,7 @@ final class superpack extends core implements record_interface
$title,
(string) $html,
(string) $text,
(int) $supercost,
(float) $supercost,
(int) $active,
svoboda::timestamp(),
svoboda::timestamp()

View File

@@ -50,6 +50,8 @@ $core->router
->write('/policy', new route('policy', 'index'), 'GET')
->write('/recomendations', new route('recomendations', 'index'), 'GET')
->write('/system/superpack/create', new route('superpack', 'create'), 'GET')
->write('/system/superpack/create', new route('superpack', 'create'), 'PUT')
->write('/superpack/$urn', new route('superpack', 'index'), 'GET')
->write('/project/request', new route('project', 'request'), 'PUT')

View File

@@ -10,6 +10,20 @@
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
class core {
/**
* @name Global modules
*
* @type {object}
*/
static global = {};
/**
* @name System modules
*
* @type {object}
*/
static system = {};
// Domain
static domain = window.location.hostname;
@@ -125,7 +139,7 @@ class core {
*
* @return {void}
*/
/* static choose = core.damper(
/* static choose = core.global.damper(
(
title = "Выбор действия",
text = "",
@@ -299,32 +313,50 @@ Object.assign(
/**
* @name Connect modules
*
* @param {(Array|string)} modules Names of modules without extension (`.mjs` only)
* @param {(Array|string)} global Names of global modules without extension (`.mjs` only)
* @param {(Array|string)} system Names of system modules without extenstion (`.mjs` only)
*
* @return {Promise}
*/
async connect(modules) {
async connect(global, system) {
// Normalisation required arguments
if (typeof modules === "string") modules = [modules];
if (typeof global === "string") global = [global];
if (typeof system === "string") system = [system];
if (modules instanceof Array) {
// Received and validated required arguments
// Initializing the registry of connected modules
const connected = {
system: [],
global: []
};
// Initializing the registry of connected modules
const connected = [];
if (global?.length > 0) {
// Global
for (const module of modules) {
// Iterating over modules
for (const module of global) {
// Iterating over global modules
// Downloading, importing and writing the module into a core property and into registry of connected modules
core[module] =
connected[module] =
// Downloading, importing and writing the global module into a core property and into registry of connected modules
core.global[module] =
connected.global[module] =
await (await import(`./modules/${module}.mjs`)).default;
}
// Exit (success)
return connected;
}
if (system?.length > 0) {
// System
for (const module of system) {
// Iterating over system modules
// Downloading, importing and writing the system module into a core property and into registry of connected modules
core.system[module] =
connected.system[module] =
await (await import(`./modules/system/${module}.mjs`)).default;
}
}
// Exit (success)
return connected;
},
},
);
@@ -347,7 +379,7 @@ core.modules.connect("damper").then(() => {
*
* @return {Promise}
*/
damper: core.damper(
damper: core.global.damper(
(...variables) => core.buffer.write.system(...variables),
300,
2,

View File

@@ -72,6 +72,37 @@ export default class paginator {
*/
#page = 1;
/**
* @name Page (get)
*
* @return {number}
*
* @public
*/
get page() {
return this.#page;
}
/**
* @name The initial page
*
* @type {number}
*
* @protected
*/
#initial = 1;
/**
* @name The initial page (get)
*
* @return {number}
*
* @public
*/
get initial() {
return this.#initial;
}
/**
* @name Constructor
*
@@ -79,16 +110,14 @@ export default class paginator {
* Initialize a hotline instance
*
* @param {HTMLElement} shell The shell element
* @param {NodeList} pages
* @param {Set} hide_on_the_first_page HTML-elements that will be hidden on the first page
* @param {Set} hide_on_the_last_page HTML-elements that will be hidden on the last page
* @param {NodeList} pages The pages HTML-elements list
* @param {number} initial The initial page identifier
* @param {boolean} [inject=false] Write the hotline instance into the shell element?
**/
constructor(
shell,
pages,
hide_on_the_first_page,
hide_on_the_last_page,
initial,
inject = false
) {
if (shell instanceof HTMLElement) {
@@ -102,11 +131,18 @@ export default class paginator {
}
if (pages instanceof NodeList) {
// Initialized pages
// Initialized the pages HTML-elements list
// Writing the pages
// Writing the pages HTML-elements list
this.#pages = pages;
}
if (typeof initial === 'number') {
// Initialized the initial page identifier
// Writing the initial page identifier
this.#page = this.#initial = initial;
}
}
/**

View File

@@ -1896,7 +1896,7 @@ export default class project {
*
* @return {void}
*/
damper: core.damper(
damper: core.global.damper(
(...variables) => this.send.system(...variables),
300,
2,
@@ -2153,20 +2153,32 @@ export default class project {
// Initializing the result HTML-element
const result = this.#elements.get('result');
// Showing the result HTML-element
result.style.removeProperty('display');
if (result instanceof HTMLElement) {
// Initialized the result HTML-element
// Showing the result HTML-element
result.style.removeProperty('display');
}
// Initializing the hours HTML-element
const output = this.#elements.get('hours_output');
// Writing into the hours output HTML-element
output.innerText = hours;
if (output instanceof HTMLElement) {
// Initialized the hours HTML-element
// Writing into the hours output HTML-element
output.innerText = hours;
}
// Initializing the hours wrap HTML-element
const wrap = this.#elements.get('hours');
// Showing the hours wrap HTML-element
wrap.style.removeProperty('display');
if (wrap instanceof HTMLElement) {
// Initialized the hours wrap HTML-element
// Showing the hours wrap HTML-element
wrap.style.removeProperty('display');
}
}
// Exit (success)
@@ -2197,8 +2209,12 @@ export default class project {
// Initializing the days HTML-element
const output = this.#elements.get('days_output');
// Writing into the days output HTML-element
output.innerText = days;
if (output instanceof HTMLElement) {
// Initialized the days HTML-element
// Writing into the days output HTML-element
output.innerText = days;
}
}
// Exit (success)
@@ -2256,24 +2272,52 @@ export default class project {
// Initializing the result HTML-element
const result = this.#elements.get('result');
// Showing the result HTML-element
result.style.removeProperty('display');
if (result instanceof HTMLElement) {
// Initialized the result HTML-element
// Initializing the output HTML-elements
// Showing the result HTML-element
result.style.removeProperty('display');
}
// Initializing the payment output HTML-element
const payment_output = this.#elements.get('payment_output');
if (payment_output instanceof HTMLElement) {
// Initialized the payment output HTML-element
// Writing into the payment output HTML-element
payment_output.innerText = new Intl.NumberFormat("ru-RU", { maximumSignificantDigits: 3 }).format(costs.full);
}
// Initializing the prepayment output HTML-element
const prepayment_output = this.#elements.get('prepayment_output');
// Writing into the output HTML-elements
payment_output.innerText = new Intl.NumberFormat("ru-RU", { maximumSignificantDigits: 3 }).format(costs.full);
prepayment_output.innerText = new Intl.NumberFormat("ru-RU", { maximumSignificantDigits: 3 }).format(costs.prepayment);
if (prepayment_output instanceof HTMLElement) {
// Initialized the prepayment output HTML-element
// Initializing the wrap HTML-elements
// Writing into the prepayment output HTML-element
prepayment_output.innerText = new Intl.NumberFormat("ru-RU", { maximumSignificantDigits: 3 }).format(costs.prepayment);
}
// Initializing the payment wrap HTML-element
const payment_wrap = this.#elements.get('payment');
if (payment_wrap instanceof HTMLElement) {
// Initialized the payment wrap HTML-element
// Showing the payment wrap HTML-element
payment_wrap.style.removeProperty('display');
}
// Initializing the prepayment wrap HTML-element
const prepayment_wrap = this.#elements.get('prepayment');
// Showing the wrap HTML-elements
payment_wrap.style.removeProperty('display');
prepayment_wrap.style.removeProperty('display');
if (prepayment_wrap instanceof HTMLElement) {
// Initialized the prepayment wrap HTML-element
// Showing the prepayment wrap HTML-element
prepayment_wrap.style.removeProperty('display');
}
}
// Exit (success)

View File

@@ -0,0 +1,552 @@
/** @module superpack */
"use strict";
/**
* @name superpack.mjs
*
* @description
* Module for creating superpacks
*
* @class
* @public
*
* {@link https://git.mirzaev.sexy/mirzaev/superpack.mjs}
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
export default class superpack {
/**
* @name Shell
*
* @description
* Shell of all elements, top-level parent HTML-element
*
* @type {HTMLElement}
*
* @protected
*/
#shell;
/**
* @name Elements
*
* @description
* Calculation steps shell elements
*
* @type {Map}
*
* @protected
*/
#elements = new Map();
/**
* @name Elements (get)
*
* @description
* Getter for `this.#elements`
*
* @return {Map}
*
* @public
*/
get elements() {
return this.#elements;
}
/**
* @name stages
*
* @description
* Active stages for guide
*
* @return {Set}
*
* @protected
*/
#stages = new Set();
/**
* @name Stages (get)
*
* @description
* Getter for `this.#stages`
*
* @return {Set}
*
* @public
*/
get stages() {
return this.#stages;
}
/**
* @name Shell (get)
*
* @description
* Getter for `this.#shell`
*
* @return {HTMLElement}
*
* @public
*/
get shell() {
return this.#shell;
}
/**
* @name URN
*
* @description
* The superpack URN (https://kodorvan.tech/superpack/{URN})
*
* @type {string}
*
* @protected
*/
#urn = '';
/**
* @name URN (set)
*
* @description
* Setter for `this.#urn`
*
* @public
*/
set urn(value) {
// Writing into the hour cost
this.#urn = value;
// Dispatching event: "system.superpack.write"
this.#shell.dispatchEvent(
new CustomEvent("system.superpack.write", {
detail: { name: 'urn', value: value }
})
);
if (this.#urn.length > 0) {
// Has a value
// Deleting from the stages registry
this.#stages.delete('urn');
} else {
// Has no value
// Writing into the stages registry
this.#stages.add('urn');
}
// Reinitializing guide HTML-elements
this.guide();
}
/**
* @name Title
*
* @description
* The superpack title
*
* @type {string}
*
* @protected
*/
#title = '';
/**
* @name Title (set)
*
* @description
* Setter for `this.#title`
*
* @public
*/
set title(value) {
// Writing into the hour cost
this.#title = value;
// Dispatching event: "system.superpack.write"
this.#shell.dispatchEvent(
new CustomEvent("system.superpack.write", {
detail: { name: 'title', value: value }
})
);
if (this.#title.length > 0) {
// Has a value
// Deleting from the stages registry
this.#stages.delete('title');
} else {
// Has no value
// Writing into the stages registry
this.#stages.add('title');
}
// Reinitializing guide HTML-elements
this.guide();
}
/**
* @name HTML
*
* @description
* The superpack content (HTML)
*
* @type {string}
*
* @protected
*/
#html = '';
/**
* @name HTML (set)
*
* @description
* Setter for `this.#html`
*
* @public
*/
set html(value) {
// Writing into the hour cost
this.#html = value;
// Dispatching event: "system.superpack.write"
this.#shell.dispatchEvent(
new CustomEvent("system.superpack.write", {
detail: { name: 'html', value: value }
})
);
if (this.#html.length > 0) {
// Has a value
// Deleting from the stages registry
this.#stages.delete('html');
} else {
// Has no value
// Writing into the stages registry
this.#stages.add('html');
}
// Reinitializing guide HTML-elements
this.guide();
}
/**
* @name Text
*
* @description
* The superpack content (text)
*
* @type {string}
*
* @protected
*/
#text = '';
/**
* @name Text (set)
*
* @description
* Setter for `this.#text`
*
* @public
*/
set text(value) {
// Writing into the hour cost
this.#text = value;
// Dispatching event: "system.superpack.write"
this.#shell.dispatchEvent(
new CustomEvent("system.superpack.write", {
detail: { name: 'text', value: value }
})
);
if (this.#text.length > 0) {
// Has a value
// Deleting from the stages registry
this.#stages.delete('text');
} else {
// Has no value
// Writing into the stages registry
this.#stages.add('text');
}
// Reinitializing guide text-elements
this.guide();
}
/**
* @name Supercost
*
* @description
* The superpack content (supercost)
*
* @type {number}
*
* @protected
*/
#supercost = 0;
/**
* @name Supercost (set)
*
* @description
* Setter for `this.#supercost`
*
* @public
*/
set supercost(value) {
// Writing into the hour cost
this.#supercost = value;
// Dispatching event: "system.superpack.write"
this.#shell.dispatchEvent(
new CustomEvent("system.superpack.write", {
detail: { name: 'supercost', value: value }
})
);
if (this.#supercost > 0) {
// Has a value
// Deleting from the stages registry
this.#stages.delete('supercost');
} else {
// Has no value
// Writing into the stages registry
this.#stages.add('supercost');
}
// Reinitializing guide supercost-elements
this.guide();
}
/**
* @name Constructor
*
* @description
* Initialize an instance
*
* @param {HTMLElement} shell The shell element
**/
constructor(
shell,
urn,
title,
html,
text,
supercost
) {
if (shell instanceof HTMLElement) {
// Initialized the shell
// Writing the shell HTML-element
this.#shell = shell;
if (urn instanceof HTMLElement) this.#elements.set('urn', urn);
if (title instanceof HTMLElement) this.#elements.set('title', title);
if (html instanceof HTMLElement) this.#elements.set('html', html);
if (text instanceof HTMLElement) this.#elements.set('text', text);
if (supercost instanceof HTMLElement) this.#elements.set('supercost', supercost);
Object.assign(
this.send,
{
/**
* @name Send (system)
*
* @description
* Send the superpack data to the server
*
* @memberof superpack.send
*
* @return {void}
*/
async system(request, resolve, reject) {
try {
if (
// typeof identifier === "string" ||
true
) {
// Validated all required arguments
return await core.request(
"/system/superpack/create",
request,
"PUT",
).then(
async (json) => {
if (json) {
// Received a JSON-response
if (
json.errors !== null &&
typeof json.errors === "object" &&
json.errors.length > 0
) {
// Fail (received errors)
// Exit (fail)
reject(json);
} else {
// Success (not received errors)
// Reloading the page @todo make something smarter
alert("Запрос доставлен");
// Exit (success)
resolve();
}
}
},
() => reject(),
);
}
} catch (e) {
console.log(e);
}
}
}
);
core?.modules.connect("damper").then((connected) => {
// Imported the damper.mjs module
Object.assign(
this.send,
{
/**
* @name Send (damper)
*
* @description
* Send the superpack data to the server
*
* @memberof superpack.send
*
* @param {booleanean} [force=false] Ignore the damper?
*
* @return {void}
*/
damper: core.damper(
(...variables) => this.send.system(...variables),
300,
2,
),
}
);
});
}
}
/**
* @name Guide
*
* @description
* Set user interface help elements
*
* @param {(string|Set)} stages
*
* @public
**/
guide(stages) {
if (stages !== undefined) {
// Received stages
if (stages instanceof Set) {
// Set
// Reinitializing stages
this.#stages = stages;
} else {
// String (expected)
// Writing into stages
this.#stages.add(stages);
}
}
for (const [parameter, element] of this.#elements) {
// Iterating over elements
// Initializing the guide HTML-element
const guide = element.querySelector('.guide');
if (guide instanceof HTMLElement) {
// Initialized the guide HTML-element
// Initializing the input HTML-element
const input = element.querySelector('.input');
if (input.value == "" || this.#stages.has(parameter)) {
// Requested guide
// Showing the guide
guide.classList.add('active');
} else {
// Not requested guide
// Hiding the guide
guide.classList.remove('active');
}
}
}
}
/**
* @name Pack
*
* @description
* Compose the superpack parameters
*
* @public
*
* @returns {string} Parameters row for `application/x-www-form-urlencoded`
*/
pack() {
// Exit (success)
return "identifier=" + encodeURIComponent(new Date().valueOf())
+ "&urn=" + encodeURIComponent(this.#urn)
+ "&title=" + encodeURIComponent(this.#title)
+ "&html=" + encodeURIComponent(this.#html)
+ "&text=" + encodeURIComponent(this.#text)
+ "&supercost=" + encodeURIComponent(this.#supercost)
;
}
/**
* @name Send
*
* @description
* Compose the superpack and send to the server
*
* @public
*
* @param {boolean} [force=false] Ignore the damper?
*/
send(force = false) {
core.modules.connect("damper").then(
() => {
// Imported the damper module
// Processing under damper
this.send.damper(this.pack(), force);
},
() => {
// Not imported the damper module
// Processing
this.send.system(this.pack());
},
);
}
}

View File

@@ -10,28 +10,28 @@ core.modules.connect(["damper", "project"]).then((connected) => {
const files = document.getElementById("project_files");
// Initializing the instance of the project.mjs
core.project = new connected.project(
document.querySelector('section[data-paginator-page="1"]'),
document.getElementById("architecture"),
document.getElementById("purpose"),
document.getElementById("integrations"),
document.getElementById("team"),
document.getElementById("programmers"),
document.getElementById("designers"),
document.getElementById("boosters"),
document.getElementById("reward"),
document.getElementById("hour"),
document.getElementById("hour_input_number"),
document.getElementById("hour_input_range"),
document.getElementById("result"),
document.getElementById("calculated"),
document.getElementById("hours"),
document.getElementById("hours_output"),
document.getElementById("days_output"),
document.getElementById("payment"),
document.getElementById("payment_output"),
document.getElementById("prepayment"),
document.getElementById("prepayment_output"),
core.global.project = new connected.global.project(
document.querySelector('section[data-paginator-page="2"]'),
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
document.getElementById("project_name"),
document.getElementById("project_description"),
files,
@@ -43,23 +43,6 @@ core.modules.connect(["damper", "project"]).then((connected) => {
true
);
// Initializing the hour input number HTML-element
const hour_number = document.getElementById('hour_input_number');
// Initializing the hour input range HTML-element
const hour_range = document.getElementById('hour_input_range');
hour_range.addEventListener("input", () => {
hour_inputs_syncronize(hour_range, hour_number);
});
function hour_inputs_syncronize(range, number) {
const min = range.min ? range.min : 300;
const max = range.max ? range.max : 5000;
number.value = range.value;
}
// Initializing the "back" button
const back = document.getElementById('back');
@@ -70,17 +53,18 @@ core.modules.connect(["damper", "project"]).then((connected) => {
const pages = document.querySelectorAll('section[data-paginator-page]');
// Initializing the instance of the paginator.mjs
core.paginator = new connected.paginator(
core.global.paginator = new connected.global.paginator(
document.getElementById('buttons'),
pages
pages,
2
);
// Initializing the buttons
const buttons = core.paginator.shell.querySelectorAll('button[data-paginator-page-button]');
const buttons = core.global.paginator.shell.querySelectorAll('button[data-paginator-page-button]');
function menu(identifier) {
// Initializing the target page button HTML-element
const active = core.paginator.shell.querySelector('button[data-paginator-page-button="' + identifier + '"]');
const active = core.global.paginator.shell.querySelector('button[data-paginator-page-button="' + identifier + '"]');
if (active instanceof HTMLElement) {
// Initialized the target page button HTML-element
@@ -95,7 +79,7 @@ core.modules.connect(["damper", "project"]).then((connected) => {
// Showing the target page button HTML-element
active.style.removeProperty('display');
if (identifier > 1) {
if (identifier > core.global.paginator.initial) {
// Second or more page
// Showing the "back" button
@@ -109,58 +93,58 @@ core.modules.connect(["damper", "project"]).then((connected) => {
}
}
// Initializing the page buttons menu
core.global.paginator.shell.addEventListener("paginator.page.opened", function (event) {
// Scrolling to the introdution HTML-element
introdution?.scrollIntoView({ behavior: 'smooth' });
// Initializing the page buttons menu
menu(event.detail.identifier);
});
// Connecting event listener for project calculation
core.project.shell.addEventListener("project.write", function() {
// Initializing the "calculator" page button HTML-element
const calculator = core.paginator.shell.querySelector('button[data-paginator-page-button="1"]');
if (calculator instanceof HTMLElement) {
// Initialized the "calculator" page button HTML-element
// Showing or hiding the "calculator" page button HTML-element
calculator.disabled = !(core.project.architecture != null);
}
core.global.project.shell.addEventListener("project.write", function() {
// Initializing the "project" page button HTML-element
const project = core.paginator.shell.querySelector('button[data-paginator-page-button="2"]');
const project = core.global.paginator.shell.querySelector('button[data-paginator-page-button="2"]');
if (project instanceof HTMLElement) {
// Initialized the "project" page button HTML-element
// Showing or hiding the "project" page button HTML-element
project.disabled =
!(core.project.project.name
!(core.global.project.project.name
&& (
core.project.project.description != null
|| core.project.project.files.length > 0
core.global.project.project.description != null
|| core.global.project.project.files.length > 0
));
}
// Initializing the "requester" page button HTML-element
const requester = core.paginator.shell.querySelector('button[data-paginator-page-button="3"]');
const requester = core.global.paginator.shell.querySelector('button[data-paginator-page-button="3"]');
if (requester instanceof HTMLElement) {
// Initialized the "requester" page button HTML-element
// Showing or hiding the "requester" page button HTML-element
requester.disabled =
!(core.project.requester.personal
!(core.global.project.requester.personal
&& (
core.project.requester.sim != null
|| core.project.requester.mail != null
|| core.project.requester.other != null
core.global.project.requester.sim != null
|| core.global.project.requester.mail != null
|| core.global.project.requester.other != null
));
}
});
// Connecting event listener for project calculation
core.project.shell.addEventListener("project.calculated", function() {
core.global.project.shell.addEventListener("project.calculated", function() {
// Showing the buttons HTML-element
core.paginator.shell?.style.removeProperty('display');
core.global.paginator.shell?.style.removeProperty('display');
});
// Initializing the page buttons menu
core.paginator.shell.addEventListener("paginator.page.opened", function (event) {
core.global.paginator.shell.addEventListener("paginator.page.opened", function (event) {
// Scrolling to the introdution HTML-element
introdution?.scrollIntoView({ behavior: 'smooth' });
@@ -215,7 +199,7 @@ core.modules.connect(["damper", "project"]).then((connected) => {
const index = Array.from(list.children).indexOf(element);
// Initializing the actual list of files
let files = core.project.project.files;
let files = core.global.project.project.files;
// Deleting the file
files.splice(index, 1);
@@ -224,7 +208,7 @@ core.modules.connect(["damper", "project"]).then((connected) => {
element.remove();
// Writing into the project property
core.project.project.files = files;
core.global.project.project.files = files;
})
// Initializing the file label delete button image
@@ -247,5 +231,5 @@ core.modules.connect(["damper", "project"]).then((connected) => {
}
})
core.project.shell.parentElement.classList.remove('loading');
core.global.project.shell.parentElement.classList.remove('loading');
});

View File

@@ -0,0 +1,251 @@
"use strict";
core.modules.connect(["damper", "project"]).then((connected) => {
// Imported modules
// Initializing the introdution HTML-element
const introdution = document.getElementById('introdution');
// Initializing the files input wrap HTML-element
const files = document.getElementById("project_files");
// Initializing the instance of the project.mjs
core.global.project = new connected.global.project(
document.querySelector('section[data-paginator-page="1"]'),
document.getElementById("architecture"),
document.getElementById("purpose"),
document.getElementById("integrations"),
document.getElementById("team"),
document.getElementById("programmers"),
document.getElementById("designers"),
document.getElementById("boosters"),
document.getElementById("reward"),
document.getElementById("hour"),
document.getElementById("hour_input_number"),
document.getElementById("hour_input_range"),
document.getElementById("result"),
document.getElementById("calculated"),
document.getElementById("hours"),
document.getElementById("hours_output"),
document.getElementById("days_output"),
document.getElementById("payment"),
document.getElementById("payment_output"),
document.getElementById("prepayment"),
document.getElementById("prepayment_output"),
document.getElementById("project_name"),
document.getElementById("project_description"),
files,
document.getElementById("requester_name"),
document.getElementById("requester_sim"),
document.getElementById("requester_mail"),
document.getElementById("requester_other"),
document.getElementById("requester_personal"),
true
);
// Initializing the hour input number HTML-element
const hour_number = document.getElementById('hour_input_number');
// Initializing the hour input range HTML-element
const hour_range = document.getElementById('hour_input_range');
hour_range.addEventListener("input", () => {
hour_inputs_syncronize(hour_range, hour_number);
});
function hour_inputs_syncronize(range, number) {
const min = range.min ? range.min : 300;
const max = range.max ? range.max : 5000;
number.value = range.value;
}
// Initializing the "back" button
const back = document.getElementById('back');
core.modules.connect(["paginator"]).then((connected) => {
// Imported the paginator.mjs module
// Initializing pages
const pages = document.querySelectorAll('section[data-paginator-page]');
// Initializing the instance of the paginator.mjs
core.global.paginator = new connected.global.paginator(
document.getElementById('buttons'),
pages
);
// Initializing the buttons
const buttons = core.global.paginator.shell.querySelectorAll('button[data-paginator-page-button]');
function menu(identifier) {
// Initializing the target page button HTML-element
const active = core.global.paginator.shell.querySelector('button[data-paginator-page-button="' + identifier + '"]');
if (active instanceof HTMLElement) {
// Initialized the target page button HTML-element
for (const inactive of buttons) {
// Iteration over pages
// Hiding the page button HTML-element
inactive.style.setProperty('display', 'none');
}
// Showing the target page button HTML-element
active.style.removeProperty('display');
if (identifier > 1) {
// Second or more page
// Showing the "back" button
back.style.removeProperty('display');
} else {
// The first page
// Hiding the "back" button
back.style.setProperty('display', 'none');
}
}
}
// Connecting event listener for project calculation
core.global.project.shell.addEventListener("project.write", function() {
// Initializing the "calculator" page button HTML-element
const calculator = core.global.paginator.shell.querySelector('button[data-paginator-page-button="1"]');
if (calculator instanceof HTMLElement) {
// Initialized the "calculator" page button HTML-element
// Showing or hiding the "calculator" page button HTML-element
calculator.disabled = !(core.global.project.architecture != null);
}
// Initializing the "project" page button HTML-element
const project = core.global.paginator.shell.querySelector('button[data-paginator-page-button="2"]');
if (project instanceof HTMLElement) {
// Initialized the "project" page button HTML-element
// Showing or hiding the "project" page button HTML-element
project.disabled =
!(core.global.project.project.name
&& (
core.global.project.project.description != null
|| core.global.project.project.files.length > 0
));
}
// Initializing the "requester" page button HTML-element
const requester = core.global.paginator.shell.querySelector('button[data-paginator-page-button="3"]');
if (requester instanceof HTMLElement) {
// Initialized the "requester" page button HTML-element
// Showing or hiding the "requester" page button HTML-element
requester.disabled =
!(core.global.project.requester.personal
&& (
core.global.project.requester.sim != null
|| core.global.project.requester.mail != null
|| core.global.project.requester.other != null
));
}
});
// Connecting event listener for project calculation
core.global.project.shell.addEventListener("project.calculated", function() {
// Showing the buttons HTML-element
core.global.paginator.shell?.style.removeProperty('display');
});
// Initializing the page buttons menu
core.global.paginator.shell.addEventListener("paginator.page.opened", function (event) {
// Scrolling to the introdution HTML-element
introdution?.scrollIntoView({ behavior: 'smooth' });
// Initializing the page buttons menu
menu(event.detail.identifier);
});
});
// Initializing the files list HTML-element
const list = files.querySelector('ol.files');
// Initializing the files input HTML-element
const files_input = files.querySelector('.input');
files_input.addEventListener('input', (event) => {
// Deleting every file label from the list
list.innerHTML = '';
if (files_input.files.length > 0) {
// Has files
// Showing the list HTML-element
list.style.removeProperty('display');
// Initializing the file index iterator
let index = 0;
for (const file of files_input.files) {
// Iterating over files
// Initializing the maximum length for the file label name
const maximum = 16;
// Initializing the end tail for the file label name
const tail = 8;
// Initializing the file label name
const name = file.name.length > maximum ? file.name.slice(0, maximum - tail).trim() + '...' + file.name.slice(-tail).trim() : file.name;
// Initializing the file label HTML-element
const element = document.createElement('li');
element.id = "project_files_input_" + index;
element.setAttribute('title', file.name);
element.innerText = name;
// Initializing the file label delete button
const button = document.createElement('button');
button.classList.add('delete');
button.addEventListener('click', (event) => {
// Initializing the file index
const index = Array.from(list.children).indexOf(element);
// Initializing the actual list of files
let files = core.global.project.project.files;
// Deleting the file
files.splice(index, 1);
// Deleting the file label HTML-element
element.remove();
// Writing into the project property
core.global.project.project.files = files;
})
// Initializing the file label delete button image
const image = document.createElement('img');
image.setAttribute('src', '/themes/default/images/icons/close.svg');
// Writing the file label into the list HTML-element
button.appendChild(image);
element.appendChild(button);
list.appendChild(element);
// Recalculating the index (postfix-incrementation)
index++;
}
} else {
// Has no files
// Hiding the list HTML-element
list.style.setProperty('display', 'none');
}
})
core.global.project.shell.parentElement.classList.remove('loading');
});

View File

@@ -0,0 +1,15 @@
"use strict";
core.modules.connect(["damper"], ["superpack"]).then((connected) => {
// Imported modules
// Initializing the instance of the project.mjs
core.system.superpack = new connected.system.superpack(
document.getElementById('superpack'),
document.getElementById('superpack_urn'),
document.getElementById('superpack_title'),
document.getElementById('superpack_html'),
document.getElementById('superpack_text'),
document.getElementById('superpack_supercost'),
);
});

View File

@@ -1,10 +1,11 @@
@charset "UTF-8";
section#project {
--width: 420px;
--margin-bottom: 0.8rem;
--width: 480px;
z-index: 200;
position: relative;
margin-top: 2rem;
margin-top: 3rem;
margin-bottom: 5rem;
min-width: var(--width);
max-width: var(--width);
@@ -32,6 +33,14 @@ section#project {
margin-bottom: 0.5em;
}
&+span {
margin-top: 0.2em;
margin-bottom: 1em;
text-align: center;
color: #e9e8e2;
text-shadow: 0px 1px 3px #000C, 0px 1px 1px #000B;
}
&+small {
margin-bottom: 1em;
text-align: center;
@@ -67,6 +76,7 @@ section#project {
--shadow: 0px 6px 6px -2px rgba(0, 0, 0, 0.5), 0px 0px 14px 5px rgba(0, 0, 0, 0.4);
position: relative;
padding: 2rem 1.4rem;
padding: 1.6rem 1.4rem;
display: flex;
flex-direction: column;
gap: 1.4rem;
@@ -854,7 +864,7 @@ section#project {
--radius: calc(var(--diameter, 4ch) / 2);
position: absolute;
left: calc(-1.2rem - var(--diameter, 4ch));
top: calc(7.4rem - var(--radius, 2ch));
top: calc(10rem - var(--radius, 2ch));
width: var(--diameter, 4ch);
height: var(--diameter, 4ch);
display: flex;
@@ -878,6 +888,7 @@ section#project {
}
>section#buttons {
margin-bottom: var(--margin-bottom, 0.8rem);
flex-direction: row;
justify-content: center;
@@ -953,6 +964,63 @@ section#project {
}
}
>small.description {
padding: 0.9em 1.2em;
text-align: left;
font-family: Nunito;
font-size: 0.9em;
color: #d6c1c1;
background: #b3ced31a;
border-radius: 1.25rem;
border-top: 1px solid #84d1d526;
backdrop-filter: blur(2px) contrast(1.14);
/* text-shadow: 0px 1px 3px #000C, 0px 1px 1px #000B; */
text-shadow: unset;
>:is(strong, b) {
/* display: block; */
font-weight: 600;
color: #ebdada;
}
}
>small.guarantee {
padding: 0.9em 1.2em;
text-align: left;
font-family: Nunito;
font-size: 0.9em;
color: #d6c1c1;
background: #b3ced31a;
border-radius: 1.25rem;
border-top: 1px solid #84d1d526;
backdrop-filter: blur(2px) contrast(1.14);
/* text-shadow: 0px 1px 3px #000C, 0px 1px 1px #000B; */
text-shadow: unset;
>:is(strong, b) {
/* display: block; */
font-weight: 600;
color: #ebdada;
}
}
>b.partners {
margin-top: 1.2rem;
padding: 0 1em;
text-align: center;
font-family: Bahnschrift;
font-weight: 600;
font-size: 1.2em;
color: #d6c1c1;
text-shadow: 0px 1px 3px #000C, 0px 1px 1px #000B;
>:is(strong, b) {
/* display: block; */
font-weight: 600;
color: #ebdada;
}
}
>small.offer {
margin-top: 1.2rem;
padding: 0 1.5em;
@@ -998,12 +1066,13 @@ section#project {
}
}
@media (width < 550px) {
@media (width < 700px) {
section#project {
--width: 100vw;
padding: 0 2rem;
>div.adaptive {
margin-bottom: var(--margin-bottom, 0.8rem);
display: flex;
flex-direction: row;
gap: 1rem;
@@ -1022,6 +1091,10 @@ section#project {
height: auto;
border-radius: 1.25rem;
}
>section#buttons {
margin-bottom: unset;
}
}
}
}

View File

@@ -21,7 +21,7 @@ body {
>article#rofls {
z-index: 50;
width: min-content;
width: 100vw;
height: 32px;
display: flex;
flex-direction: row;
@@ -36,7 +36,11 @@ body {
>img {
margin: unset;
height: 100%;
overflow: hidden;
cursor: grab;
&:is(:active) {
cursor: grabbing;
}
&:is(.noclick) {
cursor: not-allowed;

View File

@@ -154,6 +154,7 @@ footer {
>article.contacts {
min-height: 100px;
padding-left: 2rem;
display: flex;
flex-direction: column;
align-items: end;
@@ -240,6 +241,7 @@ footer {
}
>article.contacts {
padding: unset;
align-items: center;
}
}

View File

@@ -1,7 +1,7 @@
h1#logotype {
z-index: 200;
margin: unset;
margin-top: 4rem;
margin-top: 6rem;
margin-bottom: 2rem;
width: max-content;
display: flex;

View File

@@ -5,6 +5,7 @@ main {
--scroll-px-hundred: calc(var(--scroll-px-ten) / 10);
--scroll-px-thousand: calc(var(--scroll-px-hundred) / 10);
margin-top: var(--menu-height);
min-height: 100vh;
display: flex;
flex-direction: column;
flex-grow: 1;

View File

@@ -23,35 +23,4 @@ body {
}
}
}
>div.vignette {
z-index: 100;
position: fixed;
top: 0;
width: 100vw;
height: 100vh;
justify-self: center;
align-self: center;
pointer-events: none;
background: linear-gradient(90deg, #000B -20%, #0000 50%, #000B 120%);
}
>div.dots {
--dot-bg: #18333f;
--dot-color: #041825;
--dot-size: 23px;
--dot-space: 24px;
z-index: -50;
position: fixed;
top: 0;
left: min(-30vw, -300px);
justify-self: center;
align-self: center;
width: max(250vw, 600px);
height: 200vh;
rotate: -16deg;
pointer-events: none;
background: linear-gradient(90deg, var(--dot-bg) calc(var(--dot-space) - var(--dot-size)), transparent 1%) center / var(--dot-space) var(--dot-space), linear-gradient(var(--dot-bg) calc(var(--dot-space) - var(--dot-size)), transparent 1%) center / var(--dot-space) var(--dot-space), var(--dot-color);
transform-origin: top left;
}
}

View File

@@ -2,42 +2,6 @@
body {
>main {
>h1#title {
z-index: 200;
margin: unset;
margin-top: 4rem;
margin-bottom: 2rem;
width: max-content;
display: flex;
flex-direction: column;
text-align: center;
gap: 0.6rem;
font-family: "";
font-size: min(8vw, 4rem);
font-weight: 400;
/* scale: 0.8; */
color: #fff;
>span.slogan {
line-height: 0.8em;
font-family: "GOST";
font-weight: 400;
font-size: 1.24em;
color: #3688a2;
}
>a.kodorvan {
line-height: 0.8em;
font-family: "MT Sans";
font-size: 2em;
text-decoration: unset;
color: #c2ff98;
/* text-shadow:
0px 0px 4px #ffff00b5,
0px 0px 11px #ffff008a */
}
}
>article#offer {
z-index: 500;
border-radius: 1.25rem;
@@ -57,37 +21,6 @@ body {
}
}
}
>div.vignette {
z-index: 100;
position: fixed;
top: 0;
width: 100vw;
height: 100vh;
justify-self: center;
align-self: center;
pointer-events: none;
background: linear-gradient(90deg, #000B -20%, #0000 50%, #000B 120%);
}
>div.dots {
--dot-bg: #18333f;
--dot-color: #041825;
--dot-size: 23px;
--dot-space: 24px;
z-index: -50;
position: fixed;
top: 0;
left: min(-30vw, -300px);
justify-self: center;
align-self: center;
width: max(250vw, 600px);
height: 200vh;
rotate: -16deg;
pointer-events: none;
background: linear-gradient(90deg, var(--dot-bg) calc(var(--dot-space) - var(--dot-size)), transparent 1%) center / var(--dot-space) var(--dot-space), linear-gradient(var(--dot-bg) calc(var(--dot-space) - var(--dot-size)), transparent 1%) center / var(--dot-space) var(--dot-space), var(--dot-color);
transform-origin: top left;
}
}
@media (width < 1000px) {

View File

@@ -0,0 +1,172 @@
@charset "UTF-8";
body {
>main {
>article#superpack {
z-index: 500;
border-radius: 1.25rem;
margin: 3rem 0 8rem;
width: 540px;
box-sizing: border-box;
padding: 2rem;
display: flex;
flex-direction: column;
gap: 1.4rem;
background: #fff;
>h1 {
margin-top: unset;
margin-bottom: 0.2rem;
text-align: center;
font-family: "MT Sans";
font-size: 2.6rem;
font-weight: 400;
}
&:is(.loading) {
>div.loading {
opacity: 1;
height: 100%;
transition: opacity 0.1s ease-in, height 0s linear 0s;
}
}
>div.loading {
z-index: 1000;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 0%;
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
pointer-events: all;
background: #2c2c33d4;
backdrop-filter: blur(1.4px);
transition: opacity 0.2s ease-out, height 0s linear 0.2s;
&:before {
margin-top: 50px;
position: absolute;
content: var(--project-loading, "Loading");
width: max-content;
text-align: center;
font-family: "Geologica";
font-style: normal;
font-weight: 400;
font-size: 0.6rem;
color: #fff;
}
>i.icon.loading.spinner {
color: #fff;
}
}
small.guide {
padding: 0 0.2em;
display: none;
font-family: Bahnschrift;
font-size: 0.75em;
font-weight: 200;
color: #29262b;
&:is(.active) {
display: inline;
}
}
>section {
--shadow: 0px 10px 8px -4px rgba(0, 0, 0, 0.15);
position: relative;
display: flex;
flex-direction: column;
gap: 0.8em;
span {
font-family: Bahnschrift;
font-weight: 300;
}
>span {
font-size: 0.85em;
}
textarea {
margin-top: 0.2em;
min-width: 100%;
max-width: 100%;
width: 100%;
min-height: 8ch;
max-height: 60vh;
resize: vertical;
}
>:is(label, div).input.icon {
--title-height: 0.9em;
display: grid;
grid-template-columns: 1.3em auto;
grid-template-rows: var(--title-height, 1em) repeat(2, auto);
align-items: center;
grid-column-gap: 0.5em;
grid-row-gap: 0.2em;
>img.icon {
grid-column: 1;
grid-row: 2/3;
width: 100%;
}
>span.title {
grid-column: 2;
grid-row: 1;
/* margin-bottom: 0.2em; */
font-size: var(--title-height, 1em);
padding: 0 0.5em;
font-weight: 400;
font-family: 'Cascadia Code';
}
>:is(input, select, textarea) {
grid-column: 2;
grid-row: 2;
width: 100%;
border-radius: 0;
}
>small.guide {
grid-column: 2;
grid-row: 3;
margin-top: 0.2em;
}
}
>button {
padding: 1.2rem 1rem;
font-size: 1.1rem;
/* border: unset; */
&:first-of-type {
margin-top: 2rem;
}
}
}
}
}
}
@media (width < 1000px) {
body {
>main {
>article#offer {
margin-bottom: unset;
width: 100%;
border-radius: unset;
}
}
}
}

View File

@@ -48,6 +48,40 @@ body {
/* background: var(--background-color, #fff);
background: var(--background-gradient); */
background-color: #020c13;
>div.vignette {
z-index: 100;
position: fixed;
top: 0;
width: 100vw;
min-height: 5000px;
height: 100vh;
justify-self: center;
align-self: center;
pointer-events: none;
background: linear-gradient(90deg, #000B -20%, #0000 50%, #000B 120%);
transform-origin: top center;
}
>div.dots {
--dot-bg: #18333f;
--dot-color: #041825;
--dot-size: 23px;
--dot-space: 24px;
z-index: -50;
position: fixed;
top: 0;
left: min(-30vw, -300px);
justify-self: center;
align-self: center;
width: max(250vw, 600px);
min-height: 5000px;
height: 200vh;
rotate: -16deg;
pointer-events: none;
background: linear-gradient(90deg, var(--dot-bg) calc(var(--dot-space) - var(--dot-size)), transparent 1%) center / var(--dot-space) var(--dot-space), linear-gradient(var(--dot-bg) calc(var(--dot-space) - var(--dot-size)), transparent 1%) center / var(--dot-space) var(--dot-space), var(--dot-color);
transform-origin: top left;
}
}
a {
@@ -64,15 +98,27 @@ a {
&::selection {
color: #094ef2;
background: #FFF;
background: #dbd035;
text-shadow: none;
}
&::-moz-selection {
color: #094ef2;
background: #FFF;
background: #dbd035;
text-shadow: none;
}
/* &:is(.darked)::selection {
background: #000;
}
&:is(.darked)::-moz-selection {
background: #000;
} */
&:is(.tech) {
font-family: "Cascadia Code";
}
}
.unselectable {

View File

@@ -51,7 +51,7 @@
// Imported the hotline.mjs module
// Initializing an instance of the hotline.mjs
const instance = new connected.hotline(document.getElementById("cases"));
const instance = new connected.global.hotline(document.getElementById("cases"));
// Initializing settings of the hotline instance
instance.alive = true;

View File

@@ -10,7 +10,7 @@
// Imported the hotline.mjs module
// Initializing an instance of the hotline.mjs
const instance = new connected.hotline(document.getElementById("companies"));
const instance = new connected.global.hotline(document.getElementById("companies"));
// Initializing settings of the hotline instance
instance.alive = true;

View File

@@ -28,7 +28,7 @@
class="input"
name="architecture_input"
autocomplete="off"
oninput="core.project.architecture = this.value"
oninput="core.global.project.architecture = this.value"
>
<option value="" selected="true" disabled="true" hidden="true" data-project-select-title="true">Выберите архитектуру</option>
{% for value, label in project.architectures %}
@@ -45,7 +45,7 @@
class="input"
name="purpose_input"
autocomplete="off"
oninput="core.project.purpose = this.value"
oninput="core.global.project.purpose = this.value"
>
<option value="" selected="true" disabled="true" hidden="true" data-project-select-title="true">Выберите назначение</option>
{% for value, label in project.purposes %}
@@ -67,7 +67,7 @@
type="checkbox"
value=""
autocomplete="off"
onchange="this.checked ? core.project.integrate(this.value) : core.project.desintegrate(this.value)"
onchange="this.checked ? core.global.project.integrate(this.value) : core.global.project.desintegrate(this.value)"
/>
<label for="integration_input_{{ value}}">{{ label }}</label>
</div>
@@ -89,7 +89,7 @@
max="3"
value="0"
autocomplete="off"
onkeyup="core.project.programmers = this.value"
onkeyup="core.global.project.programmers = this.value"
/>
<span>Программисты</span>
</label>
@@ -102,7 +102,7 @@
max="4"
value="0"
autocomplete="off"
onkeyup="core.project.designers = this.value"
onkeyup="core.global.project.designers = this.value"
/>
<span>Дизайнеры</span>
</label>
@@ -115,7 +115,7 @@
max="2"
value="0"
autocomplete="off"
onkeyup="core.project.boosters = this.value"
onkeyup="core.global.project.boosters = this.value"
/>
<span>Бустеры</span>
</label>
@@ -135,7 +135,7 @@
max="5000"
value="1800"
autocomplete="off"
oninput="document.getElementById('hour_input_range').value = this.value; core.project.hour = this.value;"
oninput="document.getElementById('hour_input_range').value = this.value; core.global.project.hour = this.value;"
/>
<span class="currency rubles"></span>
<input
@@ -147,7 +147,7 @@
step="100"
value="1800"
autocomplete="off"
oninput="document.getElementById('hour_input_number').value = this.value; core.project.hour = this.value;"
oninput="document.getElementById('hour_input_number').value = this.value; core.global.project.hour = this.value;"
/>
</div>
<small class="guide">Введите предложение оплаты за рабочий час команды</small>
@@ -168,7 +168,7 @@
placeholder="Проект А"
value=""
autocomplete="off"
onkeyup="core.project.project.name = this.value"
onkeyup="core.global.project.project.name = this.value"
/>
<small class="guide active">Введите внутреннее название проекта</small>
</label>
@@ -184,7 +184,7 @@
value=""
autocomplete="off"
placeholder="Сделайте, чтобы всё было красиво, спасибо"
onkeyup="core.project.project.description = this.value"
onkeyup="core.global.project.project.description = this.value"
></textarea>
<small class="guide active">Опишите задачу в свободной форме</small>
</label>
@@ -204,7 +204,7 @@
multiple="true"
value=""
autocomplete="off"
oninput="core.project.project.files = this.files"
oninput="core.global.project.project.files = this.files"
/>
</div>
</div>
@@ -224,7 +224,7 @@
placeholder="Богдан"
value=""
autocomplete="off"
onkeyup="core.project.requester.name = this.value"
onkeyup="core.global.project.requester.name = this.value"
/>
<small class="guide active">Введите ваши инициалы</small>
</label>
@@ -239,7 +239,7 @@
placeholder="7 (000) 000 0000"
value=""
autocomplete="off"
onkeyup="core.project.requester.sim = this.value"
onkeyup="core.global.project.requester.sim = this.value"
/>
<small class="guide active">Введите номер телефона для связи</small>
</label>
@@ -254,7 +254,7 @@
placeholder="project@company.org"
value=""
autocomplete="off"
onkeyup="core.project.requester.mail = this.value"
onkeyup="core.global.project.requester.mail = this.value"
/>
<small class="guide active">Введите почту для связи</small>
</label>
@@ -270,7 +270,7 @@
value=""
autocomplete="off"
placeholder="Синяя программа: @zornhut"
onkeyup="core.project.requester.other = this.value"
onkeyup="core.global.project.requester.other = this.value"
></textarea>
<small class="guide active">Введите дополнительные удобные способы связи</small>
</label>
@@ -286,7 +286,7 @@
type="checkbox"
value=""
autocomplete="off"
oninput="core.project.requester.personal = this.checked"
oninput="core.global.project.requester.personal = this.checked"
/>
<img src="/themes/default/images/icons/circle.svg" alt="lock kodorvan" style="display: var(--display, none)" ondragstart="return false"/>
<img src="/themes/default/images/icons/circle_dot.svg" alt="unlock kodorvan" style="display: var(--display, none)" ondragstart="return false"/>
@@ -306,20 +306,20 @@
</article>
<div class="adaptive">
<button id="back" class="unselectable" onclick="core.paginator.relative(-1)" style="display: none">
<button id="back" class="unselectable" onclick="core.global.paginator.relative(-1)" style="display: none">
<img src="/themes/default/images/icons/arrow_left.svg" alt="back button kodorvan" ondragstart="return false"/>
</button>
<section id="buttons" style="display: none">
<button onclick="core.paginator.relative(1)" data-paginator-page-button="1">ПРОДОЛЖИТЬ</button>
<button onclick="core.paginator.relative(1)" style="display: none" data-paginator-page-button="2" disabled="true">ПРОДОЛЖИТЬ</button>
<button onclick="core.global.paginator.relative(1)" data-paginator-page-button="1">ПРОДОЛЖИТЬ</button>
<button onclick="core.global.paginator.relative(1)" style="display: none" data-paginator-page-button="2" disabled="true">ПРОДОЛЖИТЬ</button>
<button
id="send"
style="display: none"
onclick="core.project.send();"
onclick="core.global.project.send();"
data-paginator-page-button="3"
disabled="true"
>ОТПРАВИТЬ</button>
>СОЗДАТЬ ПРОЕКТ</button>
</section>
<script>

View File

@@ -0,0 +1,195 @@
<style>
:root {
--project-loading: "{{ language.name == 'ru' or true ? 'Загрузка' : 'Loading' }}";
}
</style>
<section id="project">
<div>
<h1 class="unselectable">СОЗДАНИЕ ПРОЕКТА</h1>
<span class="unselectable">Отправьте заявку и получите бесплатный<br/>расчёт с индивидуальными рекомендациями!</span>
</div>
<noscript>
<b>Калькулятор не загрузится в вашем браузере</b></br>
<i>Оформите заказ через звонок оператору по номеру {{ contacts.sim.requests.full ?? 'Ошибка' }}</i>
</noscript>
<article class="loading" aria-label="Конструктор проекта">
<div class="loading"><i class="icon loading spinner animated"></i></div>
<section data-paginator-page="2" data-paginator-page-focus="name_input" class="unselectable">
<section id="metadata">
<label id="project_name" class="input icon" for="project_name_input">
<img class="icon" src="/themes/default/images/icons/nametag.svg" alt="nametag kodorvan" ondragstart="return false"/>
<span class="title">Название</span>
<input
id="project_name_input"
class="input"
name="project_name_input"
type="text"
placeholder="Проект А"
value=""
autocomplete="off"
onkeyup="core.global.project.project.name = this.value"
/>
<small class="guide active">Введите внутреннее название проекта</small>
</label>
<label id="project_description" class="input icon" for="project_description_input">
<img class="icon" src="/themes/default/images/icons/notes.svg" alt="notes kodorvan" ondragstart="return false"/>
<span class="title">Описание</span>
<textarea
id="project_description_input"
class="input"
name="project_description_input"
cols="100%"
maxlength="4096"
value=""
autocomplete="off"
placeholder="Сделайте, чтобы всё было красиво, спасибо"
onkeyup="core.global.project.project.description = this.value"
></textarea>
<small class="guide active">Опишите задачу в свободной форме</small>
</label>
<div id="project_files" class="input icon">
<img class="icon" src="/themes/default/images/icons/import.svg" alt="import kodorvan" ondragstart="return false"/>
<div>
<span class="title">Импорт</span>
<ol style="display: none;" class="files"></ol>
<small class="guide active">Загрузите документы и изображения</small>
<label class="pseudoinput" for="project_files_input"></label>
<input
id="project_files_input"
class="input"
name="project_files_input"
type="file",
accept="image/*,.pdf,pptx,.doc,.docx,.odt,.word,.pages,.rtf,.txt"
multiple="true"
value=""
autocomplete="off"
oninput="core.global.project.project.files = this.files"
/>
</div>
</div>
</section>
</section>
<section data-paginator-page="3" data-paginator-page-focus="requester_name_input" class="unselectable" style="display: none;">
<section id="requester">
<label id="requester_name" class="input icon" for="requester_name_input">
<img class="icon" src="/themes/default/images/icons/smile.svg" alt="smile kodorvan" ondragstart="return false"/>
<span class="title">Представитель</span>
<input
id="requester_name_input"
class="input"
name="requester_name_input"
type="text"
placeholder="Богдан"
value=""
autocomplete="off"
onkeyup="core.global.project.requester.name = this.value"
/>
<small class="guide active">Введите ваши инициалы</small>
</label>
<label id="requester_sim" class="input icon" for="requester_sim_input">
<img class="icon" src="/themes/default/images/icons/phone.svg" alt="phone kodorvan" ondragstart="return false"/>
<span class="title">SIM-номер</span>
<input
id="requester_sim_input"
class="input"
name="requester_sim_input"
type="text"
placeholder="7 (000) 000 0000"
value=""
autocomplete="off"
onkeyup="core.global.project.requester.sim = this.value"
/>
<small class="guide active">Введите номер телефона для связи</small>
</label>
<label id="requester_mail" class="input icon" for="requester_mail_input">
<img class="icon" src="/themes/default/images/icons/mail.svg" alt="mail kodorvan" ondragstart="return false"/>
<span class="title">Почта</span>
<input
id="requester_mail_input"
class="input"
name="requester_mail_input"
type="email"
placeholder="project@company.org"
value=""
autocomplete="off"
onkeyup="core.global.project.requester.mail = this.value"
/>
<small class="guide active">Введите почту для связи</small>
</label>
<label id="requester_other" class="input icon"for="requester_other_input">
<img class="icon" src="/themes/default/images/icons/comment.svg" alt="comment kodorvan" ondragstart="return false"/>
<span class="title">Дополнительные контакты</span>
<textarea
id="requester_other_input"
class="input"
name="name_input"
cols="100%"
maxlength="2048"
value=""
autocomplete="off"
placeholder="Синяя программа: @zornhut"
onkeyup="core.global.project.requester.other = this.value"
></textarea>
<small class="guide active">Введите дополнительные удобные способы связи</small>
</label>
</section>
<section id="confirmation">
<label id="requester_personal" for="requester_personal_input">
<div>
<input
id="requester_personal_input"
class="input"
name="requester_personal_input"
type="checkbox"
value=""
autocomplete="off"
oninput="core.global.project.requester.personal = this.checked"
/>
<img src="/themes/default/images/icons/circle.svg" alt="lock kodorvan" style="display: var(--display, none)" ondragstart="return false"/>
<img src="/themes/default/images/icons/circle_dot.svg" alt="unlock kodorvan" style="display: var(--display, none)" ondragstart="return false"/>
</div>
<span class="title">Разрешаю запись, обработку и хранение моих персональных данных</span>
</label>
</section>
</section>
</article>
<div class="adaptive">
<button id="back" class="unselectable" onclick="core.global.paginator.relative(-1)" style="display: none">
<img src="/themes/default/images/icons/arrow_left.svg" alt="back button kodorvan" ondragstart="return false"/>
</button>
<section id="buttons">
<button onclick="core.global.paginator.relative(1)" data-paginator-page-button="2" disabled="true">ПРОДОЛЖИТЬ</button>
<button
id="send"
style="display: none"
onclick="core.global.project.send();"
data-paginator-page-button="3"
disabled="true"
>СОЗДАТЬ ПРОЕКТ</button>
</section>
<script>
const send = document.getElementById('send');
let deg = 120;
setInterval(() => send.style.setProperty('--button-send-background-color', (deg += 43) + 'deg'), 1200);
</script>
</div>
<small class="description unselectable"><strong>Команда разработчиков</strong> - мы реальные исполнители из <strong>Перми</strong>! <wbr/>Передача кода заказчику в его полную собственность и подпись <b>NDA</b>. Не используем ИИ в разработке! <wbr/><b>Полная анонимность</b> проектов - никто не узнает кто разработчик. <wbr/><b>Не оставляем</b> блокировки и скрытую рекламу в коде!</small>
<small class="guarantee unselectable"><strong>Гарантия качества</strong><b>:</b> небольшие и простые проекты мы передаём нашим студентам, но за качество кода и соблюдение сроков всегда отвечает наставник!</small>
<b class="partners unselectable">Поддерживаем проекты наших<br/>партнёров с 2014 года!</b>
<section id="contacts_shortcut">
{% include '/themes/default/interface/media.html' %}
{% include '/themes/default/interface/sim.html' %}
<small class="unselectable">ЗВОНИТЕ ПО НОМЕРУ</small>
</section>
</section>

View File

@@ -1,50 +1,50 @@
<article id="rofls" class="unselectable" style="display: none;">
<img class="construction" loading="lazy" src="https://lyra.horse/css-clicker/buttons/construction.gif"
<img class="construction" loading="lazy" src="https://lyra.horse/css-clicker/buttons/construction.gif" width="88px" height="31px"
ondragstart="return false;" />
<img loading="lazy" src="https://lyra.horse/css-clicker/buttons/css2.gif" ondragstart="return false;" />
<img loading="lazy" src="https://lyra.horse/css-clicker/buttons/dbd.gif" ondragstart="return false;" />
<!-- <img loading="lazy" src="https://lyra.horse/css-clicker/buttons/gaywebring.gif" ondragstart="return false;" /> -->
<!-- <img loading="lazy" src="https://lyra.horse/css-clicker/buttons/miku.gif" ondragstart="return false;" /> -->
<img loading="lazy" src="https://lyra.horse/css-clicker/buttons/mozbutd1.gif" ondragstart="return false;" />
<img loading="lazy" src="https://lyra.horse/css-clicker/buttons/nofuckingthanks.gif" ondragstart="return false;" />
<img class="noclick" loading="lazy" src="https://maia.crimew.gay/badges/noclick.gif" onclick="alert('bruh');"
<img loading="lazy" src="https://lyra.horse/css-clicker/buttons/css2.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://lyra.horse/css-clicker/buttons/dbd.gif" width="88px" height="31px" ondragstart="return false;" />
<!-- <img loading="lazy" src="https://lyra.horse/css-clicker/buttons/gaywebring.gif" width="88px" height="31px" ondragstart="return false;" /> -->
<!-- <img loading="lazy" src="https://lyra.horse/css-clicker/buttons/miku.gif" width="88px" height="31px" ondragstart="return false;" /> -->
<img loading="lazy" src="https://lyra.horse/css-clicker/buttons/mozbutd1.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://lyra.horse/css-clicker/buttons/nofuckingthanks.gif" width="88px" height="31px" ondragstart="return false;" />
<img class="noclick" loading="lazy" src="https://maia.crimew.gay/badges/noclick.gif" width="88px" height="31px" onclick="alert('bruh');"
ondragstart="return false;" />
<img loading="lazy" src="https://cyber.dabamos.de/88x31/zanarkand.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/antinazi.gif" ondragstart="return false;" />
<img class="hell" loading="lazy" src="https://cyber.dabamos.de/88x31/tohell.gif" ondragstart="return false;"
<img loading="lazy" src="https://cyber.dabamos.de/88x31/zanarkand.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/antinazi.gif" width="88px" height="31px" ondragstart="return false;" />
<img class="hell" loading="lazy" src="https://cyber.dabamos.de/88x31/tohell.gif" width="88px" height="31px" ondragstart="return false;"
ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/say-no-to-web3.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/they-walk-among-us.png" ondragstart="return false;" />
<img class="eye" loading="lazy" src="https://88x31.nl/gifs/uranohead.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/discord-no-way.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/chat.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/upallnight%20(3).gif" ondragstart="return false;" />
<img loading="lazy" src="https://lyra.horse/css-clicker/buttons/free-sex.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/virusalert.gif" ondragstart="return false;" />
<img loading="lazy" src="https://cyber.dabamos.de/88x31/microsoft_stop.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/linuxnow2.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/vim.vialle.love.anim.gif" ondragstart="return false;" />
<img loading="lazy" src="https://cyber.dabamos.de/88x31/tummy.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/anybrow%20(3).gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/camtime.png" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/reshirii.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/alien2.gif" ondragstart="return false;" />
<img class="antibuttons" loading="lazy" src="https://88x31.nl/gifs/anti_button.gif"
<img loading="lazy" src="https://88x31.nl/gifs/say-no-to-web3.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/they-walk-among-us.png" width="88px" height="31px" ondragstart="return false;" />
<img class="eye" loading="lazy" src="https://88x31.nl/gifs/uranohead.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/discord-no-way.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/chat.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/upallnight%20(3).gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://lyra.horse/css-clicker/buttons/free-sex.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/virusalert.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://cyber.dabamos.de/88x31/microsoft_stop.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/linuxnow2.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/vim.vialle.love.anim.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://cyber.dabamos.de/88x31/tummy.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/anybrow%20(3).gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/camtime.png" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/reshirii.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/alien2.gif" width="88px" height="31px" ondragstart="return false;" />
<img class="antibuttons" loading="lazy" src="https://88x31.nl/gifs/anti_button.gif" width="88px" height="31px"
onclick="alert('BUTTONS SUCKS!'); document.getElementById('buttons').remove();" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/fspeech96a.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/saratov.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/thatpays2.gif" ondragstart="return false;" />
<img loading="lazy" src="https://lyra.horse/css-clicker/buttons/antinft.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/tumblr_ptmmk6djdu1xwjivko5_100.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/ieshit.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/underground.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/volta.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/ocartswap_2.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/f_ckfb.gif" ondragstart="return false;" />
<img class="cogs" loading="lazy" src="https://88x31.nl/gifs/cogs.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/antifa.gif" ondragstart="return false;" />
<img loading="lazy" src="https://cyber.dabamos.de/88x31/newbuttonone.gif" ondragstart="return false;" />
<img class="colors" loading="lazy" src="https://88x31.nl/gifs/bestviewed16bit.gif" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/fspeech96a.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/saratov.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/thatpays2.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://lyra.horse/css-clicker/buttons/antinft.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/tumblr_ptmmk6djdu1xwjivko5_100.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/ieshit.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/underground.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/volta.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/ocartswap_2.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/f_ckfb.gif" width="88px" height="31px" ondragstart="return false;" />
<img class="cogs" loading="lazy" src="https://88x31.nl/gifs/cogs.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://88x31.nl/gifs/antifa.gif" width="88px" height="31px" ondragstart="return false;" />
<img loading="lazy" src="https://cyber.dabamos.de/88x31/newbuttonone.gif" width="88px" height="31px" ondragstart="return false;" />
<img class="colors" loading="lazy" src="https://88x31.nl/gifs/bestviewed16bit.gif" width="88px" height="31px" ondragstart="return false;" />
</article>
<script>
@@ -53,7 +53,7 @@
// Imported the hotline.mjs module
// Initializing an instance of the hotline.mjs
const instance = new connected.hotline(document.getElementById("rofls"), true);
const instance = new connected.global.hotline(document.getElementById("rofls"), true);
// Initializing settings of the hotline instance
instance.alive = true;

View File

@@ -23,7 +23,10 @@
</div>
<div class="information">
<h1 class="name">{{ company.name.short }}</h1>
<p class="column"><span class="tax row">{{ company.tax }}</span><span class="identifier row">{{ company.identifier }}</span></p>
<p class="column">
<span class="tax row">{{ company.tax }}</span>
<span class="identifier row">{{ company.identifier }}</span>
</p>
<p class="worktime unselectable">
{% if company.address.full is not empty %}{{ company.address.full }}{% endif %},
<span class="time from">{{ company.worktime.from }}</span>

View File

@@ -1,7 +1,7 @@
{% block meta %}
<meta charset="utf-8" />
<link rel="canonical" href="{{ uri ?? domain ? ('https://' ~ domain) : '' }}" />
<link rel="canonical" href="{{ uri ?? (domain ? ('https://' ~ domain) : '') }}" />
<title>{{ title ?? 'Кодорвань - разработка сайтов в Перми' }}</title>
<meta name="application-name" content="Кодорвань" />
@@ -18,7 +18,7 @@
<meta property="og:description" content="{{ opengraph.description ?? 'Чистый код, авангардный дизайн, крепкий бустинг и юридическая броня! Заходи на сайт и посчитай проект за 2 минуты! Рвём шаблоны!' }}" />
<meta property="og:locale" content="ru_RU" />
<meta property="og:type" content="website" />
<meta property="og:url" content="{{ uri ?? domain ? ('https://' ~ domain) : '' }}" />
<meta property="og:url" content="{{ uri ?? (domain ? ('https://' ~ domain) : '') }}" />
<meta property="og:logo" content="{{ opengraph.logo ?? 'https://kodorvan.tech/images/kodorvan.png' }}" />
{% if opengraph.image.uri is not empty %}
<meta property="og:image" content="{{ opengraph.image.uri }}" />
@@ -44,10 +44,10 @@
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:creator" content="@mirzaev_sexy" />
<meta name="twitter:site" content="@mirzaev_sexy" />
<meta name="twitter:url" content="https://kodorvan.tech/" />
<meta name="twitter:title" content="Kodorvan" />
<meta name="twitter:description" content="Programming, design, boosting and legal armor!" />
<meta name="twitter:image" content="https://kodorvan.tech/images/kodorvan_1200_630.webp" />
<meta name="twitter:url" content="{{ uri ?? (domain ? ('https://' ~ domain) : '') }}" />
<meta name="twitter:title" content="{{ twitter.title ?? 'Кодорвань - разработка сайтов в Перми' }}" />
<meta name="twitter:description" content="{{ twitter.description ?? 'Programming, design, boosting and legal armor!' }}" />
<meta name="twitter:image" content="{{ twitter.image.uri ?? 'https://kodorvan.tech/images/kodorvan_1200_630.webp' }}" />
<link rel="apple-touch-icon" sizes="57x57" href="/images/favicon/favicon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="/images/favicon/favicon-60x60.png" />
@@ -77,22 +77,26 @@
{% endfor %}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"logo": "https://kodorvan.tech/images/kodorvan.png",
"name": "Кодорвань",
"description": "Чистый код, авангардный дизайн, крепкий бустинг и юридическая броня! Заходи на сайт и посчитай проект за 2 минуты! Рвём шаблоны!",
"url": "https://kodorvan.tech",
"contactPoint": [
{ "@type": "ContactPoint",
"telephone": "+7-901-592-4211",
"contactType": "Бесплатная консультация",
"contactOption": "TollFree",
"areaServed": "RU"
}
]
}
{% if ldjson is not empty %}
{{ ldjson }}
{% else %}
{
"@context": "https://schema.org",
"@type": "Organization",
"logo": "https://kodorvan.tech/images/kodorvan.png",
"name": "Кодорвань",
"description": "Чистый код, авангардный дизайн, крепкий бустинг и юридическая броня! Заходи на сайт и посчитай проект за 2 минуты! Рвём шаблоны!",
"url": "https://kodorvan.tech",
"contactPoint": [
{ "@type": "ContactPoint",
"telephone": "+7-901-592-4211",
"contactType": "Бесплатная консультация",
"contactOption": "TollFree",
"areaServed": "RU"
}
]
}
{% endif %}
</script>
{% endblock %}

View File

@@ -12,7 +12,7 @@
{% endblock %}
{% block main %}
{% include '/themes/default/interface/logotype.html %}
{% include '/themes/default/interface/logotype.html' %}
<article id="{{ article.identifier }}" aria-label="{{ article.title }}">
<h1>{{ article.title }}</h1>

View File

@@ -39,7 +39,7 @@
{% block main %}
{% include '/themes/default/interface/logotype.html' %}
{% include '/themes/default/elements/project.html' %}
{% include '/themes/default/elements/project/short.html' %}
{% include '/themes/default/elements/companies.html' %}
{% include '/themes/default/elements/superpacks.html' %}

View File

@@ -12,7 +12,7 @@
{% endblock %}
{% block main %}
{% include '/themes/default/interface/logotype.html %}
{% include '/themes/default/interface/logotype.html' %}
<article id="offer" aria-label="Публичная оферта">
@@ -116,7 +116,7 @@
</ul>
<p>Я уведомлен, что при необходимости я могу ознакомиться с актуальной версией Политики обработки
персональных данных {{ company.name.short }} по адресу <a href="/offer" rel="nofollow noopener">https://{{ domain }}/policy</a>.</p>
персональных данных {{ company.name.short }} по адресу <a class="tech" href="/offer" rel="nofollow noopener">https://{{ domain }}/policy</a>.</p>
</div>
</article>
{% endblock %}

View File

@@ -0,0 +1,30 @@
{% extends "/themes/default/index.html" %}
{% block css %}
{{ parent() }}
<link type="text/css" rel="stylesheet" href="/css/icons/loading_spinner.css" />
<link type="text/css" rel="stylesheet" href="/themes/default/css/pages/project.css" />
<link type="text/css" rel="stylesheet" href="/themes/default/css/elements/project.css" />
<link type="text/css" rel="stylesheet" href="/themes/default/css/elements/cookies.css" />
<link rel="preload" as="image" href="/themes/default/images/icons/close.svg" />
{% endblock %}
{% block before %}
{% endblock %}
{% block main %}
{% include '/themes/default/interface/logotype.html' %}
{% include '/themes/default/elements/project/full.html' %}
{% endblock %}
{% block after %}
<div class="vignette"></div>
<div class="dots"></div>
{% endblock %}
{% block js %}
{{ parent() }}
<script src="/js/pages/project/create.js" type="module" defer></script>
{% endblock %}

View File

@@ -0,0 +1,115 @@
{% extends "/themes/default/index.html" %}
{% block css %}
{{ parent() }}
<link type="text/css" rel="stylesheet" href="/css/icons/loading_spinner.css" />
<link type="text/css" rel="stylesheet" href="/themes/default/css/pages/system/superpack/create.css" />
<link type="text/css" rel="stylesheet" href="/themes/default/css/elements/cookies.css" />
{% endblock %}
{% block before %}
{% endblock %}
{% block main %}
{% include '/themes/default/interface/logotype.html' %}
<article id="superpack" aria-label="Форма создания суперпака">
<h1 class="unselectable">СОЗДАНИЕ СУПЕРПАКА</h1>
<section class="unselectable">
<label id="superpack_urn" class="input icon" for="superpack_urn_input">
<img class="icon" src="/themes/default/images/icons/nametag.svg" alt="nametag kodorvan" ondragstart="return false"/>
<span class="title">URN</span>
<input
id="superpack_urn_input"
class="input domain"
name="superpack_urn_input"
type="text"
placeholder="chat-bot-market"
value=""
autocomplete="off"
onkeyup="core.system.superpack.urn = this.value"
/>
<small class="guide active">Введите URN суперпака</small>
</label>
<label id="superpack_title" class="input icon" for="superpack_title_input">
<img class="icon" src="/themes/default/images/icons/notes.svg" alt="notes kodorvan" ondragstart="return false"/>
<span class="title">Название</span>
<input
id="superpack_title_input"
class="input"
name="superpack_title_input"
type="text"
placeholder="Чат-бот для магазина"
value=""
autocomplete="off"
onkeyup="core.system.superpack.title = this.value"
/>
<small class="guide active">Введите название суперпака</small>
</label>
<label id="superpack_text" class="input icon" for="superpack_text_input">
<img class="icon" src="/themes/default/images/icons/notes.svg" alt="notes kodorvan" ondragstart="return false"/>
<span class="title">Описание</span>
<textarea
id="superpack_text_input"
class="input"
name="superpack_text_input"
cols="100%"
maxlength="4096"
value=""
autocomplete="off"
placeholder="Готовое техническое решение для открытия новой точки продаж в сети"
onkeyup="core.system.superpack.text = this.value"
></textarea>
<small class="guide active">Введите описание суперпака</small>
</label>
<label id="superpack_html" class="input icon" for="superpack_html_input">
<img class="icon" src="/themes/default/images/icons/notes.svg" alt="notes kodorvan" ondragstart="return false"/>
<span class="title">Описание в формате HTML</span>
<textarea
id="superpack_html_input"
class="input"
name="superpack_html_input"
cols="100%"
maxlength="4096"
value=""
autocomplete="off"
placeholder="<p>Готовое техническое решение для открытия новой точки продаж в сети</p>"
onkeyup="core.system.superpack.html = this.value"
></textarea>
<small class="guide active">Введите описание суперпака в формате HTML</small>
</label>
<label id="superpack_supercost" class="input icon" for="superpack_supercost_input">
<img class="icon" src="/themes/default/images/icons/notes.svg" alt="notes kodorvan" ondragstart="return false"/>
<span class="title">Стоимость</span>
<input
id="superpack_supercost_input"
class="input"
name="superpack_supercost_input"
type="number"
placeholder="2000"
value=""
autocomplete="off"
onkeyup="core.system.superpack.supercost = this.value"
/>
<small class="guide active">Введите стоимость суперпака</small>
</label>
<button
id="send"
onclick="core.system.superpack.send();"
>Отправить на сервер</button>
</section>
</article>
{% endblock %}
{% block after %}
<div class="vignette"></div>
<div class="dots"></div>
{% endblock %}
{% block js %}
{{ parent() }}
<script src="/js/pages/system/superpack.js" type="module" defer></script>
{% endblock %}