diff --git a/.gitignore b/.gitignore
index 1cbbe14..2279778 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,8 @@
-**/node_modules/
-**.DS_Store
-**/.vs/
-**/dist/
-**/.idea
+**/node_modules/
+**.DS_Store
+**/.vs/
+**/dist/
+**/.idea
+**/.sublime-project
+**/.sublime-workspace
+**/_log
diff --git a/config/config.php b/config/config.php
new file mode 100644
index 0000000..e2b1ca8
--- /dev/null
+++ b/config/config.php
@@ -0,0 +1,25 @@
+ 'sum',
+ '-' => 'sub',
+ '*' => 'mul',
+ '/' => 'div'
+ ];
+
+ $arg1 = preg_split('/[\+\-\*\/]+/', $str)[0];
+ $arg2 = preg_split('/[\+\-\*\/]+/', $str)[1];
+ $operation = preg_split('/[^\+\-\*\/]+/', $str)[1];
+
+ return mathOperation($arg1, $arg2, $operations[$operation]);
+}
+
+function sum($arg1, $arg2){
+ return $arg1 + $arg2;
+}
+
+function sub($arg1, $arg2){
+ return $arg1 - $arg2;
+}
+
+function mul($arg1, $arg2){
+ return $arg1 * $arg2;
+}
+
+function div($arg1, $arg2){
+ if($arg2 == 0) {
+ return 'Ошибка! На ноль делить нельзя.';
+ }
+ return $arg1 / $arg2;
+}
+
+function mathOperation($arg1, $arg2, $operation){
+ if(function_exists($operation)){
+ return $operation($arg1, $arg2);
+ }
+}
diff --git a/engine/catalog.php b/engine/catalog.php
new file mode 100644
index 0000000..cb2e1d3
--- /dev/null
+++ b/engine/catalog.php
@@ -0,0 +1,108 @@
+ addToCart($id, $session_id),
+ 'count' => ++$count,
+ 'total' => $total
+ ]
+ );
+ break;
+
+ case 'delete':
+ echo json_encode(
+ [
+ 'deleted' => deleteFromCart($id, $session_id),
+ 'count' => --$count,
+ 'total' => $total
+ ]
+ );
+ break;
+
+ case 'checkout':
+
+ echo json_encode(
+ [
+ 'ordered' => makeAnOrder($session_id, $total, $login),
+ ]
+ );
+ break;
+ }
+ die();
+}
+
+function makeAnOrder($session_id, $total, $login):bool {
+ $result = json_decode(file_get_contents('php://input'));
+ $date = date("Y-m-d H:i");
+ executeQuery("UPDATE `cart` SET `cart_status` = 1 WHERE session_id = '{$session_id}'");
+ return executeQuery("INSERT INTO `orders` (login, name, number, mail, session_id, date, total_amount) VALUES ( '{$login}','{$result->name}', '{$result->number}', '{$result->email}', '{$session_id}', '{$date}', '{$total}')");
+}
+
+function getCart($session_id, $cart_status = 0):array {
+
+ $cart = getAssocResult("SELECT cart.id cart_id, catalog.image, catalog.id catalog_id, catalog.name, catalog.price FROM cart, catalog WHERE cart.good_id=catalog.id AND session_id = '{$session_id}' AND cart_status = {$cart_status}");
+
+ $each = getEachCartItemsCount($session_id);
+
+ $tmp = [];
+ $unique = [];
+
+ foreach ($cart as $good) {
+ if (!in_array($good['catalog_id'], $tmp)) {
+ $unique[] = $good;
+ $tmp[] = $good['catalog_id'];
+ }
+ }
+
+ function replace($a, $b) {
+ if ($a['catalog_id'] === $b['catalog_id']) {
+ $a['count'] = $b['count'];
+ }
+
+ $a['sub_total'] = (INT)str_replace(' ', '', $a['price']) * $b['count'];
+ return $a;
+ }
+
+ return array_map('replace', $unique, $each);
+}
+
+function getAllCartItemsCount($session_id):string {
+ return getAssocResult("SELECT COUNT(id) as count FROM `cart` WHERE `session_id`='{$session_id}' AND cart_status = 0")[0]['count'];
+}
+
+function getEachCartItemsCount($session_id):array {
+// $sql = "SELECT `catalog`.id AS good_id, SUM(`catalog`.price) AS total FROM `cart` INNER JOIN `catalog` ON `catalog`.id = `cart`.good_id GROUP BY `cart`.id";
+ return getAssocResult("SELECT `good_id` AS `catalog_id` , COUNT(`good_id`) AS count FROM `cart` WHERE `session_id` = '$session_id' AND cart_status = 0 GROUP BY (good_id)");
+}
+
+function getTotalPrice($cart) {
+ $total = 0;
+ foreach ($cart as $item) {
+ $total += $item['sub_total'];
+ }
+ return $total;
+}
+
+function addToCart($id, $session_id):bool {
+ return executeQuery("INSERT INTO `cart` (good_id, session_id) VALUES ($id, '$session_id')");
+}
+
+function deleteFromCart($good_id, $session_id):bool {
+ return executeQuery("DELETE FROM `cart` WHERE good_id = '{$good_id}' AND session_id = '{$session_id}' AND cart_status = 0 LIMIT 1");
+}
\ No newline at end of file
diff --git a/engine/classSimpleImage.php b/engine/classSimpleImage.php
new file mode 100644
index 0000000..85bac52
--- /dev/null
+++ b/engine/classSimpleImage.php
@@ -0,0 +1,85 @@
+image_type = $image_info[2];
+ if( $this->image_type == IMAGETYPE_JPEG ) {
+ $this->image = imagecreatefromjpeg($filename);
+ } elseif( $this->image_type == IMAGETYPE_GIF ) {
+ $this->image = imagecreatefromgif($filename);
+ } elseif( $this->image_type == IMAGETYPE_PNG ) {
+ $this->image = imagecreatefrompng($filename);
+ }
+ }
+ function save($filename, $image_type=IMAGETYPE_JPEG, $compression=75, $permissions=null) {
+ if( $image_type == IMAGETYPE_JPEG ) {
+ imagejpeg($this->image,$filename,$compression);
+ } elseif( $image_type == IMAGETYPE_GIF ) {
+ imagegif($this->image,$filename);
+ } elseif( $image_type == IMAGETYPE_PNG ) {
+ imagepng($this->image,$filename);
+ }
+ if( $permissions != null) {
+ chmod($filename,$permissions);
+ }
+ }
+ function output($image_type=IMAGETYPE_JPEG) {
+ if( $image_type == IMAGETYPE_JPEG ) {
+ imagejpeg($this->image);
+ } elseif( $image_type == IMAGETYPE_GIF ) {
+ imagegif($this->image);
+ } elseif( $image_type == IMAGETYPE_PNG ) {
+ imagepng($this->image);
+ }
+ }
+ function getWidth() {
+ return imagesx($this->image);
+ }
+ function getHeight() {
+ return imagesy($this->image);
+ }
+ function resizeToHeight($height) {
+ $ratio = $height / $this->getHeight();
+ $width = $this->getWidth() * $ratio;
+ $this->resize($width,$height);
+ }
+ function resizeToWidth($width) {
+ $ratio = $width / $this->getWidth();
+ $height = $this->getheight() * $ratio;
+ $this->resize($width,$height);
+ }
+ function scale($scale) {
+ $width = $this->getWidth() * $scale/100;
+ $height = $this->getheight() * $scale/100;
+ $this->resize($width,$height);
+ }
+ function resize($width,$height) {
+ $new_image = imagecreatetruecolor($width, $height);
+ imagecopyresampled($new_image, $this->image, 0, 0, 0, 0, $width, $height, $this->getWidth(), $this->getHeight());
+ $this->image = $new_image;
+ }
+}
\ No newline at end of file
diff --git a/engine/db.php b/engine/db.php
new file mode 100644
index 0000000..fdb14f4
--- /dev/null
+++ b/engine/db.php
@@ -0,0 +1,41 @@
+name, $data->feedback, $data->good_ID);
+ $response = [
+ 'id' => $id,
+ 'name' => $data->name,
+ 'feedback' => $data->feedback
+ ];
+
+ echo json_encode($response, JSON_UNESCAPED_UNICODE);
+ break;
+
+ case 'read':
+ echo json_encode(getOneFeedback(), JSON_UNESCAPED_UNICODE);
+ break;
+
+ case 'update':
+ $response = updateFeedback($data);
+ echo json_encode(['updated' => $response]);
+ break;
+
+ case 'delete':
+ $response = deleteFeedback($data);
+ echo json_encode(['deleted' => $response]);
+ break;
+ }
+ die();
+}
+
+function createFeedback($name, $feedback, $product_id) {
+ $name = strip_tags(htmlspecialchars(mysqli_real_escape_string(getDb(), $name)));
+ $feedback = strip_tags(htmlspecialchars(mysqli_real_escape_string(getDb(), $feedback)));
+ $sql = "INSERT INTO `feedback` (`name`, `feedback`, `product_id`) VALUES ('{$name}', '{$feedback}', '{$product_id}');";
+ executeQuery($sql);
+ return mysqli_insert_id(getDb());
+}
+
+function readFeedback() {
+ return json_decode(file_get_contents('php://input'));
+}
+
+function updateFeedback($data):bool {
+ return executeQuery("UPDATE `feedback` SET `name` = '{$data->name}', `feedback` = '{$data->feedback}' WHERE `feedback`.`id` = {$data->id}");
+}
+
+function deleteFeedback($id):bool {
+ $id = (INT)$id;
+ return executeQuery("DELETE FROM `feedback` WHERE `feedback`.`id` = {$id}");
+}
+
+function getOneFeedback():array {
+ $id = (INT)json_decode(file_get_contents('php://input'));
+ $sql = "SELECT * FROM feedback WHERE id = {$id}";
+ return getAssocResult($sql)[0];
+
+}
\ No newline at end of file
diff --git a/engine/functions.php b/engine/functions.php
new file mode 100644
index 0000000..05b7e5e
--- /dev/null
+++ b/engine/functions.php
@@ -0,0 +1,144 @@
+ 'item_feedback', 'feedback' => 'feedback'];
+ switch ($page) {
+
+ case 'auth':
+ $params['layout'] = 'auth';
+ if (isAdmin()) {
+ $params['orders'] = getAllOrders();
+ } else {
+ if (isset($params['user'])) {
+ $params['orders'] = getOrdersByLogin($params['user']);
+ }
+ }
+ break;
+
+//todo [order-details]
+
+// case 'orderDetails':
+// $orderID = (INT)$_GET['id'];
+// $params['orderDetails'] = getOrderDetails($orderID, $params['user']);
+// break;
+
+ case 'updateOrder':
+ $id = (INT)$_GET['id'];
+ if (isAdmin()) {
+ $params['order'] = getOneOrder($id);
+ }
+ if (!empty($_POST) && isAdmin()) {
+ $params['updated'] = updateOrderInfo($_POST);
+ }
+ break;
+
+ case 'signUp':
+ if (!empty($_POST)) {
+ $params['error'] = validateData($_POST);
+ if (!$params['error']) {
+ $params['signUp'] = signUp($_POST);
+ }
+ }
+ break;
+
+ case 'catalog':
+ $params['layout'] = 'catalog';
+ $params['catalog'] = getCatalog();
+ break;
+
+ case 'good':
+ $id = (INT)$_GET['id'];
+ $params['layout'] = 'catalog';
+ $params['good'] = getOneGood($id);
+ $params['feedback'] = getAllFeedback($id);
+ break;
+
+ case 'cart':
+ $params['layout'] = 'catalog';
+ $params['cart'] = getCart($params['session_id']);
+ $params['total'] = getTotalPrice($params['cart']);
+ if (isset($params['user'])) {
+ $params['user_info'] = getAuthorizedUserInfo($params['user']);
+ }
+ break;
+
+ case 'cartapi':
+ cartAction($action, $params['session_id'], $params['user']);
+ break;
+
+ case 'image':
+ $params['layout'] = 'gallery';
+ $params['image'] = getOneImage((int)$_GET['id']);
+// $params['tableName'] = getAllFeedback('item_feedback');
+ break;
+
+ case 'addLike':
+ $params['layout'] = 'catalog';
+ addLikes((int)$_GET['id']);
+ break;
+
+ case 'gallery':
+ if(isset($_POST['load'])){
+ $params['errors'] = uploadImage();
+ }
+ $params['layout'] = 'gallery';
+ $params['gallery'] = getGallery();
+ break;
+
+ case 'feedback':
+ $params['layout'] = 'feedback';
+ $params['feedback'] = getAllFeedback();
+ break;
+
+ case 'feedbackapi':
+ feedbackAction($action);
+ break;
+
+ case 'calculator':
+ if (!empty($_POST['output'])) {
+ $params['output'] = parseOperations($_POST['output']);
+ }
+ $params['layout'] = 'calculator';
+ break;
+
+ case 'homework_3':
+ $params['layout'] = 'homework_3';
+ break;
+ }
+ return $params;
+}
+
+function render($page, $params){
+ return renderTemplate(LAYOUTS_DIR . $params['layout'], [
+ 'content' => renderTemplate($page, $params),
+ 'menu' => renderTemplate('menu', $params),
+ ]
+ );
+}
+
+function renderTemplate($page, $params = []){
+ ob_start();
+
+ if (!is_null($params))
+ extract($params);
+
+ $fileName = TEMPLATES_DIR . $page . ".php";
+
+ if (file_exists($fileName)) {
+ include $fileName;
+ } else {
+ Die("Страницы {$fileName} не существует.");
+ }
+
+ return ob_get_clean();
+}
diff --git a/engine/gallery.php b/engine/gallery.php
new file mode 100644
index 0000000..cb2524c
--- /dev/null
+++ b/engine/gallery.php
@@ -0,0 +1,48 @@
+ 1024 * 5 * 1024) {
+ return "Размер файла не более 5 Мб";
+ }
+
+ $blacklist = ['.php', '.phtml', '.php3', '.php4'];
+ foreach ($blacklist as $item) {
+ if(preg_match("/$item\$/i", $_FILES['image']['name'])){
+ return "Загрузка php-файлов запрещена";
+ }
+ }
+
+ if (move_uploaded_file($_FILES['image']['tmp_name'], $path_big)) {
+
+ $filename = mysqli_real_escape_string(getDb(), $_FILES['image']['name']);
+ executeQuery("INSERT INTO `gallery` (`filename`) VALUES ('{$filename}')");
+
+ $image = new SimpleImage();
+ $image->load($path_big);
+ $image->resizeToWidth(250);
+ $image->save($path_small);
+ header("Location: /gallery");
+ } else {
+ return "Ошибка ресайза файла";
+ }
+}
\ No newline at end of file
diff --git a/engine/log.php b/engine/log.php
new file mode 100644
index 0000000..e69de29
diff --git a/engine/service.php b/engine/service.php
new file mode 100644
index 0000000..ee0c8d7
--- /dev/null
+++ b/engine/service.php
@@ -0,0 +1,85 @@
+ "Логин уже занят!",
+ "password" => "Пароли не совпадают!",
+ "phone" => "Данный номер телефона зарегистрирован!",
+ "email" => "Email уже зарегистрирован!"
+ ];
+
+ if ($data['password'] !== $data['password_confirm']) {
+ return $errorsTemplate['password'];
+ }
+ if (getAssocResult("SELECT * FROM `users` WHERE `login` = '{$data['login']}'")) {
+ return $errorsTemplate['login'];
+ }
+ if (getAssocResult("SELECT * FROM `users` WHERE `phone` = '{$data['phone']}'")) {
+ return $errorsTemplate['phone'];
+ }
+ if (getAssocResult("SELECT * FROM `users` WHERE `email` = '{$data['email']}'")) {
+ return $errorsTemplate['email'];
+ }
+ return false;
+}
+
+function signUp($data):bool {
+ $data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
+ return executeQuery("INSERT INTO `users` (login, full_name, phone, email, password) VALUES ('{$data['login']}', '{$data['full_name']}', '{$data['phone']}', '{$data['email']}', '{$data['password']}')");
+}
+
+/*
+ * actions for admin
+ */
+
+function getOneOrder($id):array {
+ return getAssocResult("SELECT * FROM `orders` WHERE `id` = $id")[0];
+}
+
+function updateOrderInfo($info):bool {
+ return executeQuery("UPDATE `orders` SET `name` = '{$info['name']}', `number` = '{$info['number']}', `mail` = '{$info['email']}', `order_status` = {$info['status']} WHERE `id` = {$info['id']}");
+}
+
+function getAllOrders():array{
+ return getAssocResult("SELECT * FROM `orders`");
+}
+
+//todo [order-details]
+//function getOrderDetails($orderID, $login):array {
+// if (isAdmin()) {
+// return getCart(getOrderSessionByID($orderID), 1);
+// }
+// return getCart(getOrderSessionByLogin($login), 1);
+//}
+
+//todo [order-details]
+
+/* пример запроса, который мог бы решить проблему с корзинами, но необходим уникальный ключ для каждой
+ * "SELECT DISTINCT orders.*, cart.good_id
+ * FROM orders INNER JOIN cart ON orders.session_id = cart.session_id
+ * INNER JOIN catalog ON cart.good_id = catalog.id
+ * WHERE orders.id = 35"
+ * */
+//function getOrderSessionByLogin($login) {
+// return getAssocResult("SELECT session_id FROM `orders` WHERE login = '{$login}'")[0]['session_id'];
+//}
+
+//todo [order-details]
+//function getOrderSessionByID($orderID) {
+// return getAssocResult("SELECT session_id FROM `orders` WHERE id = $orderID")[0]['session_id'];
+//}
\ No newline at end of file
diff --git a/engine/setup.php b/engine/setup.php
new file mode 100644
index 0000000..b0697f5
--- /dev/null
+++ b/engine/setup.php
@@ -0,0 +1,21 @@
+num_rows == 0) {
+ echo "Таблица пустая. Заполнение данными об изображениях";
+ $imgs = scandir(IMG);
+ $images = array_splice($imgs, 2);
+ mysqli_query($db, "INSERT INTO `gallery`(`filename`) VALUES ('" . implode("'),('", $images) . "')");
+} else {
+ echo "Таблица заполнена";
+}
\ No newline at end of file
diff --git a/public/.htaccess b/public/.htaccess
new file mode 100644
index 0000000..d9c1e47
--- /dev/null
+++ b/public/.htaccess
@@ -0,0 +1,10 @@
+RewriteEngine on
+
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+
+php_value error_reporting "E_ALL & ~E_NOTICE"
+
+php_value display_errors on
+
+RewriteRule . index.php
\ No newline at end of file
diff --git a/public/images/catalog_img/big/honor.jpg b/public/images/catalog_img/big/honor.jpg
new file mode 100644
index 0000000..f29face
Binary files /dev/null and b/public/images/catalog_img/big/honor.jpg differ
diff --git a/public/images/catalog_img/big/huawei.jpg b/public/images/catalog_img/big/huawei.jpg
new file mode 100644
index 0000000..a3ee080
Binary files /dev/null and b/public/images/catalog_img/big/huawei.jpg differ
diff --git a/public/images/catalog_img/big/iphone.jpg b/public/images/catalog_img/big/iphone.jpg
new file mode 100644
index 0000000..cb5b167
Binary files /dev/null and b/public/images/catalog_img/big/iphone.jpg differ
diff --git a/public/images/catalog_img/big/oppo.jpg b/public/images/catalog_img/big/oppo.jpg
new file mode 100644
index 0000000..5ddbe15
Binary files /dev/null and b/public/images/catalog_img/big/oppo.jpg differ
diff --git a/public/images/catalog_img/big/poco.jpg b/public/images/catalog_img/big/poco.jpg
new file mode 100644
index 0000000..0724555
Binary files /dev/null and b/public/images/catalog_img/big/poco.jpg differ
diff --git a/public/images/catalog_img/big/samsung.jpg b/public/images/catalog_img/big/samsung.jpg
new file mode 100644
index 0000000..002cf3e
Binary files /dev/null and b/public/images/catalog_img/big/samsung.jpg differ
diff --git a/public/images/catalog_img/small/honor.jpg b/public/images/catalog_img/small/honor.jpg
new file mode 100644
index 0000000..7a5a183
Binary files /dev/null and b/public/images/catalog_img/small/honor.jpg differ
diff --git a/public/images/catalog_img/small/huawei.jpg b/public/images/catalog_img/small/huawei.jpg
new file mode 100644
index 0000000..d1bbc47
Binary files /dev/null and b/public/images/catalog_img/small/huawei.jpg differ
diff --git a/public/images/catalog_img/small/iphone.jpg b/public/images/catalog_img/small/iphone.jpg
new file mode 100644
index 0000000..ec2b771
Binary files /dev/null and b/public/images/catalog_img/small/iphone.jpg differ
diff --git a/public/images/catalog_img/small/oppo.jpg b/public/images/catalog_img/small/oppo.jpg
new file mode 100644
index 0000000..2dea5de
Binary files /dev/null and b/public/images/catalog_img/small/oppo.jpg differ
diff --git a/public/images/catalog_img/small/poco.jpg b/public/images/catalog_img/small/poco.jpg
new file mode 100644
index 0000000..9e6bdf5
Binary files /dev/null and b/public/images/catalog_img/small/poco.jpg differ
diff --git a/public/images/catalog_img/small/samsung.jpg b/public/images/catalog_img/small/samsung.jpg
new file mode 100644
index 0000000..7743c3e
Binary files /dev/null and b/public/images/catalog_img/small/samsung.jpg differ
diff --git a/public/images/gallery_img/big/n_70.jpg b/public/images/gallery_img/big/n_70.jpg
new file mode 100644
index 0000000..d1c5b21
Binary files /dev/null and b/public/images/gallery_img/big/n_70.jpg differ
diff --git a/public/images/gallery_img/big/n_72.jpg b/public/images/gallery_img/big/n_72.jpg
new file mode 100644
index 0000000..aa1d97c
Binary files /dev/null and b/public/images/gallery_img/big/n_72.jpg differ
diff --git a/public/images/gallery_img/big/n_73.jpg b/public/images/gallery_img/big/n_73.jpg
new file mode 100644
index 0000000..e36fb36
Binary files /dev/null and b/public/images/gallery_img/big/n_73.jpg differ
diff --git a/public/images/gallery_img/big/n_76.jpg b/public/images/gallery_img/big/n_76.jpg
new file mode 100644
index 0000000..cff5f93
Binary files /dev/null and b/public/images/gallery_img/big/n_76.jpg differ
diff --git a/public/images/gallery_img/big/n_82.jpg b/public/images/gallery_img/big/n_82.jpg
new file mode 100644
index 0000000..7b92921
Binary files /dev/null and b/public/images/gallery_img/big/n_82.jpg differ
diff --git a/public/images/gallery_img/big/n_95.jpg b/public/images/gallery_img/big/n_95.jpg
new file mode 100644
index 0000000..b6929b1
Binary files /dev/null and b/public/images/gallery_img/big/n_95.jpg differ
diff --git a/public/images/gallery_img/small/n_70.jpg b/public/images/gallery_img/small/n_70.jpg
new file mode 100644
index 0000000..3184065
Binary files /dev/null and b/public/images/gallery_img/small/n_70.jpg differ
diff --git a/public/images/gallery_img/small/n_72.jpg b/public/images/gallery_img/small/n_72.jpg
new file mode 100644
index 0000000..f6105c7
Binary files /dev/null and b/public/images/gallery_img/small/n_72.jpg differ
diff --git a/public/images/gallery_img/small/n_73.jpg b/public/images/gallery_img/small/n_73.jpg
new file mode 100644
index 0000000..e14dfb8
Binary files /dev/null and b/public/images/gallery_img/small/n_73.jpg differ
diff --git a/public/images/gallery_img/small/n_76.jpg b/public/images/gallery_img/small/n_76.jpg
new file mode 100644
index 0000000..4a8ee15
Binary files /dev/null and b/public/images/gallery_img/small/n_76.jpg differ
diff --git a/public/images/gallery_img/small/n_82.jpg b/public/images/gallery_img/small/n_82.jpg
new file mode 100644
index 0000000..0636790
Binary files /dev/null and b/public/images/gallery_img/small/n_82.jpg differ
diff --git a/public/images/gallery_img/small/n_95.jpg b/public/images/gallery_img/small/n_95.jpg
new file mode 100644
index 0000000..88e8903
Binary files /dev/null and b/public/images/gallery_img/small/n_95.jpg differ
diff --git a/public/index.php b/public/index.php
new file mode 100644
index 0000000..827183a
--- /dev/null
+++ b/public/index.php
@@ -0,0 +1,19 @@
+ this._eventHandler(e));
+ // this._form.addEventListener('submit', e => {
+ // e.preventDefault();
+ // this._postData(e);
+ // });
+ }
+
+ _eventHandler(event) {
+
+ const eventText = event.target.textContent;
+
+ if (eventText === '=' || eventText.length > 1) return;
+ if(this._output.length > this._maxLengthOfInput) return;
+ if(event.target.id === 'delete') { this._delete(); return; }
+
+ const reg = new RegExp("[+\\-*\\/]", "g");
+
+ this._output += eventText;
+ if ((this._output.match(reg) || []).length > 1) {
+ this._output = this._output.slice(0, this._output.length - 1);
+ }
+ this._render();
+ }
+
+ _delete() {
+ this._output = this._calcOutput.value = this._outputNode.textContent = this._outputNode.textContent.slice(0,-1);
+ }
+
+ _render(){
+ this._outputNode.textContent = this._output;
+ this._calcOutput.value = this._output;
+ }
+}
+
+(new Calculator).init();
\ No newline at end of file
diff --git a/public/js/cart.js b/public/js/cart.js
new file mode 100644
index 0000000..863a75a
--- /dev/null
+++ b/public/js/cart.js
@@ -0,0 +1,146 @@
+'use strict';
+
+class Cart {
+ constructor() {
+ this._cartActionButtons = document.querySelectorAll('.catalog__item-button');
+ this._cartCheckoutButton = document.querySelector('.checkout__button');
+ this._fieldList = ["name", "number", "email"];
+ }
+
+ init() {
+ this._cartActionButtons.forEach(button => {
+ button.addEventListener('click', event => {
+ this._eventHandler(event);
+ });
+ });
+
+ if (typeof(this._cartCheckoutButton) != "undefined" && this._cartCheckoutButton !== null) {
+ this._cartCheckoutButton.addEventListener('click', event => {
+ this._eventHandler(event);
+ });
+ }
+ }
+
+ _eventHandler(event) {
+ switch (event.target.dataset.id) {
+ case 'buy':
+
+ this._addToCart(this._getProductId(event.path))
+ .then(json => {
+ this._cartCountRender(json);
+ this._cartEachCountRender(json, event);
+ this._cartTotalAmountRender(json);
+ })
+ .catch(e => console.error(e));
+
+ break;
+
+ case 'delete':
+
+ this._deleteFromCart(this._getProductId(event.path))
+ .then(json => {
+ this._cartCountRender(json);
+ this._cartEachCountRender(json, event);
+ this._cartTotalAmountRender(json);
+ })
+ .catch(e => console.error(e));
+
+ break;
+
+ case 'checkout':
+ this._makeAnOrder(this._readValueData(this._cartCheckoutButton, this._fieldList))
+ .catch(e => console.log(e));
+ break;
+ }
+ }
+
+ async _makeAnOrder(data) {
+ return await (await fetch(`/cartapi/checkout/`, {
+ method: 'POST',
+ headers: new Headers({
+ 'Content-Type': 'application/json'
+ }),
+ body: JSON.stringify(data)
+ })).json();
+ }
+
+ _readValueData(buttonNode, fieldList) {
+ const data = {};
+ let inputValues = this._getInputValues(buttonNode);
+ const fieldValues = [...inputValues].map((item) => item.value);
+
+ for (let i = 0; i < fieldValues.length - 1; i++) {
+ data[fieldList[i]] = `${fieldValues[i]}`;
+ }
+ return data;
+ }
+
+ _getInputValues(node) {
+ return node.parentNode.children;
+ }
+
+ async _deleteFromCart(id) {
+ return await (await fetch('/cartapi/delete/?id=' + id)).json();
+ }
+
+ async _addToCart(id) {
+ return await (await fetch('/cartapi/add/?id=' + id)).json();
+ }
+
+ _cartTotalAmountRender(json) {
+ const target = document.querySelector('.total');
+ const numberFormat = new Intl.NumberFormat();
+
+ if (json.total === 0) {
+ const cartElem = document.querySelector('.cart');
+ this._getParentElement(target).remove();
+ cartElem.innerHTML = 'В корзине ничего нет
¯\\_(ツ)_/¯';
+ return json;
+ }
+
+ if (target) {
+ target.innerText = numberFormat.format(json.total) + " ";
+ }
+ return json;
+ }
+
+ _cartEachCountRender(json, event) {
+ if (!event.target.parentElement.classList.contains('cart-buttons_wrapper')) return json;
+ const target = event.target.parentElement.children[1];
+
+ if (json.hasOwnProperty('added')) {
+ target.innerText++;
+ } else {
+ target.innerText--;
+ }
+
+ if (target.innerText === '0') {
+ //remove paragraph with info about good in checkout div
+ const itemParentId = this._getParentElement(target).id;
+ document.getElementById('checkoutID:' + itemParentId).remove();
+ //remove good from cart
+ this._getParentElement(event.target).remove();
+ return json;
+ }
+ }
+
+ _cartCountRender(json) {
+ const navButtons = document.querySelector('.nav-ul');
+ const targetElement = navButtons.children[navButtons.children.length - 2].firstElementChild;
+ targetElement.innerText = targetElement.innerText.replace(new RegExp(/\d/), json.count);
+ return json;
+ }
+
+ _getParentElement(element) {
+ if (!element.id) return this._getParentElement(element.parentElement);
+ return element;
+ }
+
+ _getProductId(path) {
+ for (let node of path) {
+ if (node.id) return node.id;
+ }
+ }
+}
+
+new Cart().init();
\ No newline at end of file
diff --git a/public/js/cartRealizationWithJS.js b/public/js/cartRealizationWithJS.js
new file mode 100644
index 0000000..adaf190
--- /dev/null
+++ b/public/js/cartRealizationWithJS.js
@@ -0,0 +1,91 @@
+'use strict';
+
+class Cart {
+ constructor() {
+ this._buyButtons = document.querySelectorAll('.catalog__item-button');
+ this._cart = {};
+ this._currentCartItems = 0;
+ this._cookieCart = 'cart';
+ this._cookieCountName = 'count';
+ }
+ init() {
+ this._buyButtons.forEach(button => {
+ button.addEventListener('click', event => {
+ this._eventHandler(event);
+ });
+ });
+ //todo refactor get cookie with 1 request
+ if (this._getCookie(this._cookieCountName)) {
+ this._cart = JSON.parse(this._getCookie(this._cookieCart));
+ this._currentCartItems = Number(this._getCookie(this._cookieCountName));
+ this._cartCountRender();
+ }
+ }
+ _eventHandler(event) {
+ this._addToCart(this._getProductId(event.path));
+ this._setCookie(this._cookieCart, this._cart);
+ this._setCookie(this._cookieCountName, this._currentCartItems);
+ this._cartCountRender();
+ }
+ _cartCountRender() {
+ const navButtons = document.querySelector('.nav-ul');
+ const targetElement = navButtons.children[navButtons.children.length - 1].firstElementChild;
+ targetElement.innerText = targetElement.innerText.replace(new RegExp(/\d/), this._currentCartItems);
+ }
+ _setCookie(name, value, options = {}) {
+ options = {
+ path: '/',
+ ...options
+ };
+
+ if (options.expires instanceof Date) {
+ options.expires = options.expires.toUTCString();
+ }
+
+ value = JSON.stringify(value);
+
+ let updatedCookie = encodeURIComponent(name) + "=" + encodeURIComponent(value);
+
+ for (let optionKey in options) {
+ updatedCookie += "; " + optionKey;
+ let optionValue = options[optionKey];
+ if (optionValue !== true) {
+ updatedCookie += "=" + optionValue;
+ }
+ }
+
+ document.cookie = updatedCookie;
+ }
+ _getCookie(name) {
+ let matches = document.cookie.match(new RegExp(
+ "(?:^|; )" + name.replace(/([.$?*|{}()\[\]\\\/+^])/g, '\\$1') + "=([^;]*)"
+ ));
+ return matches ? decodeURIComponent(matches[1]) : undefined;
+ }
+ _deleteCookie(name) {
+ this._setCookie(name, "", {
+ 'max-age': -1
+ });
+ }
+ _deleteFromCart(id) {
+ this._currentCartItems--;
+ }
+ _addToCart(id) {
+ this._currentCartItems++;
+ if (Object.keys(this._cart).length === 0) return this._cart[`${id}`] = 1;
+ return this._cart[`${id}`] ? this._cart[`${id}`] += 1 : this._cart[`${id}`] = 1;
+ }
+ getCart(){
+ return this._cart;
+ }
+ _getProductId(path) {
+ for (let node of path) {
+ if (node.id) return node.id;
+ }
+ }
+}
+
+let cart = new Cart();
+cart.init();
+
+//notice goods should add in to cart with backend only because need to synchronization with db
\ No newline at end of file
diff --git a/public/js/feedback.js b/public/js/feedback.js
new file mode 100644
index 0000000..3ec223f
--- /dev/null
+++ b/public/js/feedback.js
@@ -0,0 +1,184 @@
+'use strict';
+const actionButtons = document.querySelector('.feedback-wrapper');
+const feedbackNode = document.querySelector('.feedback-side');
+const fieldList = ["name", "feedback", "id", "good_id"];
+const feedbackID = getFeedbackID();
+
+actionButtons.addEventListener('click', (e) => handleButtonsCRUD(e));
+
+async function handleButtonsCRUD(e) {
+ const buttonNode = e.path[0];
+ if (!buttonNode.id) return;
+ const action = buttonNode.id;
+
+ switch (action) {
+ case 'create':
+ if (!validateValueData(buttonNode)) return;
+ create(buttonNode, action);
+ break;
+
+ case 'read':
+ read(buttonNode, action);
+ break;
+
+ case 'update':
+ if (!validateValueData(buttonNode)) return;
+ update(buttonNode, action);
+ break;
+
+ case 'delete':
+ remove(buttonNode, action);
+ break;
+ }
+}
+
+//CRUD BLOCK
+function create(buttonNode, action){
+ const data = readValueData(buttonNode, fieldList);
+
+ fetchData(data, action)
+ .then(response => response.json())
+ .then(json => {
+ clearInputValues(getInputValues(buttonNode));
+ renderNewFeedback(json);
+ toggleSideVisibility();
+ })
+ .catch(e => console.log(e));
+}
+
+function read(buttonNode, action) {
+ getOneFeedback(Number(getParent(buttonNode).id), action)
+ .catch(e => console.error(e));
+}
+
+function update(node, action) {
+ const data = readValueData(node, fieldList);
+ fetchData(data, action)
+ .then(response => response.json())
+ .then(json => {
+ if (json.updated) {
+ removeFeedbackById(data.id);
+ renderNewFeedback(data);
+ clearInputValues(getInputValues(node));
+ }
+ })
+ .catch(e => console.log(e));
+}
+
+function remove(buttonNode, action) {
+ fetchData(Number(getParent(buttonNode).id), action)
+ .then(res => res.json())
+ .then(json => {
+ if (json.deleted) {
+ getParent(buttonNode).remove();
+ toggleSideVisibility();
+ }
+ });
+}
+
+//OTHER FUNCTIONS
+function validateValueData(node){
+ for (const [, value] of Object.entries(readValueData(node, fieldList))) {
+ if (typeof(value) == "undefined" && value == null) return false;
+ }
+ return true;
+}
+
+function readValueData(buttonNode, fieldList) {
+ const data = {};
+ let inputValues = getInputValues(buttonNode);
+ const fieldValues = [...inputValues].map((item) => item.value);
+
+ for (let i = 0; i < fieldValues.length - 1; i++) {
+ data[fieldList[i]] = `${fieldValues[i]}`;
+ }
+ data['good_ID'] = feedbackID;
+ return data;
+}
+
+function removeFeedbackById(id){
+ [...feedbackNode.children].forEach(node => {
+ if (node.id === id) node.remove();
+ });
+}
+
+async function getOneFeedback(id, action) {
+ return await fetchData(id, action)
+ .then(response => response.json())
+ .then(json => {
+ renderFeedbackToUpdate(json)
+ });
+}
+
+async function fetchData(data, action) {
+ return await fetch(`/feedbackapi/${action}/`, {
+ method: 'POST',
+ headers: new Headers({
+ 'Content-Type': 'application/json'
+ }),
+ body: JSON.stringify(data)
+ });
+}
+
+function renderFeedbackToUpdate(feedback){
+ let html = `
+
${feedback.name}
+${feedback.feedback}
+ += $_GET['authError'] ?>
+ + + +У вас пока ни одного заказа ¯\_(ツ)_/¯
+ +Заказ #=$order['id']?>
+Имя заказчика: = $order['name'] ?>
+Телефон заказчика: = $order['number'] ?>
+Email заказчика:= $order['mail'] ?>
+Дата и время заказа: = $order['date'] ?>
+Статус заказа: = $order['order_status']?>
+Сумма заказа: = $order['total_amount'] ?> ₽
+ + + +
+
+ = $good['name'] ?>
+= $good['description'] ?>
+Цена: = $good['price'] ?> р
+ += $value['name'] ?>
+= $value['feedback'] ?>
+ += $params['error'] ?>
+С помощью цикла while вывести все числа в промежутке от 0 до 100, которые делятся на 3 без остатка.
С помощью цикла do…while написать функцию для вывода чисел от 0 до 10.
Объявить массив, в котором в качестве ключей будут использоваться названия областей, а в качестве значений – массивы с названиями городов из соответствующей области. Вывести в цикле значения массива. + Вывести на экран только города, начинающиеся с буквы «К»
Объявить массив, индексами которого являются буквы русского языка, а значениями – соответствующие латинские буквосочетания.
Заказ #=$order['id']?>
+ + + + + +Дата и время заказа: = $order['date'] ?>
+ +Сумма заказа: = $order['total_amount'] ?> ₽
+ +