This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.
Messages - efko
1
« on: 09 November 2025, 22:20:30 »
Spoiler for modalV1 JS :
import { correctName } from "./helper.js"; import { createProduct } from "./api.js"; const createProductNameInput = document.getElementById("name"); const createProductPriceInput = document.getElementById("price"); const createProductImageUrlInput = document.getElementById("image"); const createProductDescriptionInput = document.getElementById("description"); const createProductCharacteristicKeyInput = document.getElementById( "characteristics-key" ); const createProductCharacteristicValueInput = document.getElementById( "characteristics-value" ); const errorParagraph = document.createElement("p"); const createProductBtn = document.getElementsByClassName("create-product-btn")[0]; const articleContainer = document.getElementsByTagName("article")[0]; const newProduct = { name: null, price: -1, image: null, description: null, characteristics: {}, }; articleContainer.addEventListener("click", function (event) { const clickedElement = event.target; console.log(clickedElement); if ( clickedElement.matches(".delete-btn") || clickedElement.matches("svg") || clickedElement.matches("path") ) { const span = clickedElement.closest(".key-value-span"); const key = span.innerText.split(": ")[0]; delete newProduct.characteristics[key]; console.log(newProduct); span.remove(); } }); function setUpListeners() { removeEventListeners(); createProductNameInput.addEventListener("blur", validation.productName); createProductPriceInput.addEventListener("blur", validation.productPrice); createProductImageUrlInput.addEventListener( "blur", validation.productImageUrl ); createProductDescriptionInput.addEventListener( "blur", validation.productDesctiption ); createProductCharacteristicKeyInput.addEventListener( "blur", validation.productCharacteristicKey ); createProductCharacteristicValueInput.addEventListener( "blur", validation.productCharacteristicValue ); createProductBtn.removeEventListener("click", createNewProduct); createProductBtn.addEventListener("click", createNewProduct); function createNewProduct(event) { if ( newProduct.name && newProduct.price && newProduct.image && newProduct.description ) { createProduct(newProduct, "http://localhost:3000/products"); articleContainer.innerHTML = ""; } resetInputs(); } } function removeEventListeners() { createProductNameInput.removeEventListener("blur", validation.productName); createProductPriceInput.removeEventListener("blur", validation.productPrice); createProductImageUrlInput.removeEventListener( "blur", validation.productImageUrl ); createProductDescriptionInput.removeEventListener( "blur", validation.productDesctiption ); createProductCharacteristicKeyInput.removeEventListener( "blur", validation.productCharacteristicKey ); createProductCharacteristicValueInput.removeEventListener( "blur", validation.productCharacteristicValue ); } const validation = { productName: function (event) { removeErrorMsg(); const nameInputValue = event.target.value.trim(); if (nameInputValue.length > 3) { newProduct.name = correctName(nameInputValue); // console.log(newProduct); } else { renderError(createProductNameInput, "Product name is not correct."); } }, productPrice: function (event) { removeErrorMsg(); const priceInputValue = parseFloat(event.target.value); // console.log(priceInputValue); if (priceInputValue > 0 && priceInputValue < 10000) { newProduct.price = priceInputValue; // console.log(newProduct); } else { renderError( createProductPriceInput, "Product price is not in defined range." ); // console.log("Product price is not in defined range."); } }, productImageUrl: function (event) { removeErrorMsg(); const imageInputValue = event.target.value; const allowedProtocols = ["www", "http://", "https://"]; const allowedExtensions = [".png", ".jpg", ".jpeg"]; let isCorrectProtocol = false; let isCorrectExtension = false; for (const protocol of allowedProtocols) { if (imageInputValue.startsWith(protocol)) { isCorrectProtocol = true; } } for (const extension of allowedExtensions) { if (imageInputValue.endsWith(extension)) { isCorrectExtension = true; } } if (isCorrectExtension && isCorrectProtocol) { newProduct.image = imageInputValue; // console.log(newProduct); } else { renderError(createProductImageUrlInput, "Url is not valid!"); // console.log("Url is not valid!"); } }, productDesctiption: function (event) { removeErrorMsg(); const descriptionInputValue = event.target.value; if ( descriptionInputValue.length > 20 && descriptionInputValue.length < 200 ) { newProduct.description = descriptionInputValue; // console.log(newProduct); } else { renderError( createProductDescriptionInput, "Description is not defined well!" ); // console.log("Description is not defined well!"); } }, productCharacteristicKey: function (event) { const keyInputValue = event.target.value.trim(); handleNewCharacteristics( keyInputValue, createProductCharacteristicValueInput.value ); }, productCharacteristicValue: function (event) { const valueInputValue = event.target.value.trim(); handleNewCharacteristics( createProductCharacteristicKeyInput.value, valueInputValue ); }, }; function handleNewCharacteristics(key, value) { removeErrorMsg(); if (key && value) { createCharacteristicsElement(key, value); newProduct.characteristics[key] = value; resetCharacteristics(); console.log(newProduct); } else { renderError(articleContainer, "Key or value is not defined!"); } function resetCharacteristics() { createProductCharacteristicKeyInput.value = ""; createProductCharacteristicValueInput.value = ""; } } function createCharacteristicsElement(key, value) { const spanEl = ` <span class="key-value-span" >${key}: ${value} <div class="delete-btn"> <svg width="15px" height="15px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" > <g id="SVGRepo_bgCarrier" stroke-width="0"></g> <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" ></g> <g id="SVGRepo_iconCarrier"> <g id="Menu / Close_MD"> <path id="Vector" d="M18 18L12 12M12 12L6 6M12 12L18 6M12 12L6 18" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" ></path> </g> </g> </svg> </div> </span>`; articleContainer.insertAdjacentHTML("beforeend", spanEl); } function resetInputs() { createProductNameInput.value = ""; createProductPriceInput.value = ""; createProductImageUrlInput.value = ""; createProductDescriptionInput.value = ""; createProductCharacteristicKeyInput.value = ""; createProductCharacteristicValueInput.value = ""; } function renderError(element, msg) { errorParagraph.classList.add("errParagraph"); errorParagraph.innerText = msg; element.insertAdjacentElement("afterend", errorParagraph); } function removeErrorMsg() { errorParagraph.remove(); } export { setUpListeners }; //ZADATAK: //napisati drugi nacin za kreiranje karakteristika u newProduct.characteristic, tako da se pri kliku na dugme "create product" , pokupe sve vrednosti i tek tad dodaju u objekat //2.zadatak //registracija i logovanje
2
« on: 09 November 2025, 22:19:45 »
Spoiler for modal JS :
import { correctName } from "./helper.js"; const createProductNameInput = document.getElementById("name"); const createProductPriceInput = document.getElementById("price"); const createProductImageUrlInput = document.getElementById("image"); const createProductDescriptionInput = document.getElementById("description"); const createProductCharacteristicKeyInput = document.getElementById( "characteristics-key" ); const createProductCharacteristicValueInput = document.getElementById( "characteristics-value" ); const articleContainer = document.getElementsByTagName("article")[0]; const newProduct = { name: null, price: -1, image: null, description: null, characteristics: {}, }; articleContainer.addEventListener("click", function (event) { const clickedElement = event.target; console.log(clickedElement); if ( clickedElement.matches(".delete-btn") || clickedElement.matches("svg") || clickedElement.matches("path") ) { const span = clickedElement.closest(".key-value-span"); const key = span.innerText.split(": ")[0]; delete newProduct.characteristics[key]; console.log(newProduct); span.remove(); } }); createProductNameInput.addEventListener("blur", function (event) { const nameInputValue = correctName(event.target.value.trim()); if (nameInputValue.length > 3) { newProduct.name = nameInputValue; console.log(newProduct); } else { console.log("Product name input is empty."); } }); createProductPriceInput.addEventListener("blur", function (event) { const priceInputValue = parseFloat(event.target.value); console.log(priceInputValue); if (priceInputValue > 0 && priceInputValue < 10000) { newProduct.price = priceInputValue; console.log(newProduct); } else { console.log("Product price is not in defined range."); } }); createProductImageUrlInput.addEventListener("blur", function (event) { const imageInputValue = event.target.value; const allowedProtocols = ["www", "http://", "https://"]; const allowedExtensions = [".png", ".jpg", ".jpeg"]; let isCorrectProtocol = false; let isCorrectExtension = false; for (const protocol of allowedProtocols) { if (imageInputValue.startsWith(protocol)) { isCorrectProtocol = true; } } for (const extension of allowedExtensions) { if (imageInputValue.endsWith(extension)) { isCorrectExtension = true; } } if (isCorrectExtension && isCorrectProtocol) { newProduct.image = imageInputValue; console.log(newProduct); } else { console.log("Url is not valid!"); } }); createProductDescriptionInput.addEventListener("blur", function (event) { const descriptionInputValue = event.target.value; if (descriptionInputValue.length > 20 && descriptionInputValue.length < 200) { newProduct.description = descriptionInputValue; console.log(newProduct); } else { console.log("Description is not defined well!"); } }); createProductCharacteristicKeyInput.addEventListener("blur", function (event) { const keyInputValue = event.target.value.trim(); handleNewCharacteristics( keyInputValue, createProductCharacteristicValueInput.value ); }); createProductCharacteristicValueInput.addEventListener( "blur", function (event) { const valueInputValue = event.target.value.trim(); handleNewCharacteristics( createProductCharacteristicKeyInput.value, valueInputValue ); } ); function handleNewCharacteristics(key, value) { if (key && value) { createCharacteristicsElement(key, value); newProduct.characteristics[key] = value; resetCharacteristics(); console.log(newProduct); } function resetCharacteristics() { createProductCharacteristicKeyInput.value = ""; createProductCharacteristicValueInput.value = ""; } } function createCharacteristicsElement(key, value) { const spanEl = ` <span class="key-value-span" >${key}: ${value} <div class="delete-btn"> <svg width="15px" height="15px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" > <g id="SVGRepo_bgCarrier" stroke-width="0"></g> <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" ></g> <g id="SVGRepo_iconCarrier"> <g id="Menu / Close_MD"> <path id="Vector" d="M18 18L12 12M12 12L6 6M12 12L18 6M12 12L6 18" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" ></path> </g> </g> </svg> </div> </span>`; articleContainer.insertAdjacentHTML("beforeend", spanEl); } function resetInputs() { createProductNameInput.value = ""; createProductPriceInput.value = ""; createProductImageUrlInput.value = ""; createProductDescriptionInput.value = ""; createProductCharacteristicKeyInput.value = ""; createProductCharacteristicValueInput.value = ""; }
3
« on: 09 November 2025, 22:19:08 »
4
« on: 09 November 2025, 22:18:17 »
Spoiler for style CSS :
* { box-sizing: border-box; } body { margin: 0; position: relative; min-height: 100vh; width: 100%; background-color: rgb(245, 255, 253); padding-bottom: 5rem; } body header { padding: 1rem; background-color: rgb(2, 130, 100); display: flex; justify-content: center; align-items: center; margin-bottom: 1rem; } body header h1 { margin: 0; color: white; } body main { display: flex; justify-content: center; align-items: center; flex-direction: column; padding: 1rem; } body main .search-section { padding: 1rem; margin-bottom: 1rem; width: 100%; text-align: center; } body main .search-section input { padding: 0.5rem; border-radius: 5px; box-shadow: 0 0 10px rgb(2, 130, 100); font-size: 1.1rem; width: 90%; } body .container-section { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; flex-wrap: wrap; gap: 1.5rem; } body .container-section .product { border: 1px solid rgb(2, 130, 100); border-radius: 10px; width: 25%; min-width: 350px; box-shadow: 0 0 10px rgb(2, 130, 100); color: rgb(1, 73, 56); } body .container-section .product .product-img { width: 100%; max-height: 300px; } body .container-section .product .product-info { padding: 0.5rem; display: flex; justify-content: center; align-items: center; align-content: center; flex-direction: column; } body .container-section .product .product-info .product-name { margin: 0; } body .container-section .product .product-info .product-price { margin: 0.5rem; } body .container-section .product .product-info p { margin: 0.5rem; } body .container-section .product:hover { transform: scale(1.03); } body footer { position: absolute; bottom: 0; background-color: rgb(2, 130, 100); width: 100%; } body footer p { color: white; display: flex; justify-content: center; align-items: center; text-transform: uppercase; font-weight: 600; } body .btn-add { position: fixed; bottom: 5rem; right: 3rem; width: 80px; height: 80px; border-radius: 50%; background-color: rgb(158, 2, 2); } body .btn-add div { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; } body .btn-add svg { width: 70%; height: 70%; } body .container-modal-body { display: flex; flex-direction: column; align-items: center; } body .container-modal-body .errParagraph { margin-top: 5px; color: red; margin: 0; } body .container-modal-body #name, body .container-modal-body #price, body .container-modal-body #image, body .container-modal-body #description { box-sizing: border-box; width: 90%; font-size: 1.2rem; } body .container-modal-body .characteristics { width: 90%; } body .container-modal-body .characteristics div { width: 100%; display: flex; justify-content: center; align-items: center; justify-content: space-around; margin-bottom: 1rem; } body .container-modal-body .characteristics div input { width: 40%; } body .container-modal-body article { display: flex; gap: 1rem; width: 90%; margin: 0 auto; flex-wrap: wrap; } body .container-modal-body article .key-value-span { position: relative; text-align: center; padding: 5px 10px; background-color: rgb(255, 0, 0); color: white; border-radius: 10px; } body .container-modal-body article .key-value-span .delete-btn { position: absolute; top: -8px; right: -8px; width: 20px; height: 20px; border-radius: 50%; background-color: white; border: 1px solid black; display: flex; justify-content: center; align-items: center; align-items: center; }/*# sourceMappingURL=style.css.map */
Spoiler for style SCSS :
$font-color: white; $default-color: rgb(2, 130, 100); @mixin centar() { display: flex; justify-content: center; align-items: center; } * { box-sizing: border-box; } body { margin: 0; position: relative; min-height: 100vh; width: 100%; background-color: rgb(245, 255, 253); padding-bottom: 5rem; header { padding: 1rem; background-color: $default-color; @include centar(); margin-bottom: 1rem; h1 { margin: 0; color: $font-color; } } main { @include centar(); flex-direction: column; padding: 1rem; .search-section { padding: 1rem; margin-bottom: 1rem; width: 100%; text-align: center; input { padding: 0.5rem; border-radius: 5px; box-shadow: 0 0 10px $default-color; font-size: 1.1rem; width: 90%; } } } .container-section { width: 100%; height: 100%; @include centar(); flex-wrap: wrap; gap: 1.5rem; .product { border: 1px solid $default-color; border-radius: 10px; width: 25%; min-width: 350px; box-shadow: 0 0 10px $default-color; color: rgb(1, 73, 56); .product-img { width: 100%; max-height: 300px; } .product-info { padding: 0.5rem; @include centar(); align-content: center; flex-direction: column; .product-name { margin: 0; } .product-price { margin: 0.5rem; } p { margin: 0.5rem; } } &:hover { transform: scale(1.03); } } } footer { position: absolute; bottom: 0; background-color: $default-color; width: 100%; p { color: $font-color; @include centar(); text-transform: uppercase; font-weight: 600; } } .btn-add { position: fixed; bottom: 5rem; right: 3rem; width: 80px; height: 80px; border-radius: 50%; background-color: rgb(158, 2, 2); div { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; } svg { width: 70%; height: 70%; } } .container-modal-body { display: flex; flex-direction: column; align-items: center; .errParagraph { margin-top: 5px; color: red; margin: 0; } #name, #price, #image, #description { box-sizing: border-box; width: 90%; font-size: 1.2rem; } .characteristics { width: 90%; div { width: 100%; @include centar(); justify-content: space-around; margin-bottom: 1rem; input { width: 40%; } } } article { display: flex; gap: 1rem; width: 90%; margin: 0 auto; flex-wrap: wrap; .key-value-span { position: relative; text-align: center; // width: 100px; padding: 5px 10px; // height: 30px; background-color: rgb(255, 0, 0); color: white; border-radius: 10px; .delete-btn { position: absolute; top: -8px; right: -8px; width: 20px; height: 20px; border-radius: 50%; background-color: white; border: 1px solid black; @include centar(); align-items: center; } } } } }
5
« on: 09 November 2025, 22:17:01 »
Spoiler for main JS :
import { truncateData, debaunce } from "./helper.js"; import { fetchData } from "./api.js"; import { setUpListeners } from "./modal-v1.js"; const containerProducts = document.getElementsByClassName("container-section")[0]; const searchInput = document.getElementById("search-input"); const url = "http://localhost:3000/products"; function renderProducts(products) { products = filterData(products); containerProducts.innerHTML = products .map((product) => { return ` <div class="product"> <img class="product-img" src="${product.image}" alt="${ product.name }" /> <div class="product-info"> <h2 class="product-name">${product.name}</h2> <h4 class="product-price">${product.price} $</h4> <p class="product-description">${truncateData( product.description )}</p> <p class="product-characteristics">${ product.characteristics ? getCharacteristics(product.characteristics) : "No additional attributes." }</p> </div> </div>`; }) .join(""); function getCharacteristics(obj) { let template = ""; for (const [key, value] of Object.entries(obj)) { //[['color', 'Cloudy White'], ['capacity', '512 GB']] template += `<p>${key}: ${value}</p>`; } return template; } } function filterData(data) { const searchValue = searchInput.value.toLowerCase().trim(); const filteredData = data.filter((item) => item.name.toLowerCase().includes(searchValue) ); return filteredData; } function searchInputLogic() { fetchData(renderProducts, url); } document.addEventListener("DOMContentLoaded", function () { fetchData(renderProducts, url); const debaunceLogic = debaunce(searchInputLogic, 2000); searchInput.addEventListener("input", function (event) { debaunceLogic(); }); setUpListeners(); });
6
« on: 09 November 2025, 22:16:16 »
Spoiler for products JSON :
{ "products": [ { "id": "1", "name": "Google Pixel 6 Pro", "price": 899.99, "image": "https://amateurphotographer.com/wp-content/uploads/sites/7/2021/11/pixel-6-pro-rear-P1088727_cropped.jpg?w=435&h=350&crop=1", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "color": "Cloudy White", "capacity": "128 GB" } }, { "id": "2", "name": "Apple iPhone 12 Mini, 256GB, Blue", "price": 699.99, "image": "https://www.tehnomedia.rs/image/71882.jpg?tip=webp&tip_slike=0", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": null }, { "id": "3", "name": "Apple iPhone 12 Pro Max", "price": 1099.99, "image": "https://www.dxomark.com/wp-content/uploads/medias/post-61584/iphone-12-pro-max-graphite-hero.jpg", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "color": "Cloudy White", "capacity": "512 GB" } }, { "id": "4", "name": "Apple iPhone 11, 64GB", "price": 389.99, "image": "https://www.winwin.rs/media/catalog/product/cache/aa01455892d233ac3aea63acd876143d/1/1/1131867_000000000001131867-main-webp-580-conversion-media-format_1.webp", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "color": "Purple" } }, { "id": "5", "name": "Samsung Galaxy Z Fold2", "price": 689.99, "image": "https://images.samsung.com/is/image/samsung/p5/au/smartphones/galaxy-z-fold2/buy/002_Z-Fold2-MysticBlack-dynamic-GalleryImage-MO-img80.jpg?$ORIGIN_JPG$?imbypass=true", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "color": "Brown" } }, { "id": "6", "name": "Apple AirPods", "price": 120, "image": "https://www.apple.com/newsroom/images/tile-images/Apple-AirPods-world-most-popular-wireless-headphones_03202019.jpg.landing-big_2x.jpg", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "generation": "3rd" } }, { "id": "7", "name": "Apple MacBook Pro 16", "price": 1849.99, "image": "https://techpoets.com/img-product/428/apple-macbook-pro-16-m1-pro-1tb.jpg", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "year": 2019, "cpu model": "Intel Core i9", "hard disk size": "1 TB" } }, { "id": "8", "name": "Apple Watch Series 8", "price": 399.99, "image": "https://cdn.mos.cms.futurecdn.net/mfJNvNwN8CjPvLF7H8fSnQ.jpg", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "strap Colour": "Elderberry", "case Size": "41mm" } }, { "id": "9", "name": "Beats Studio3 Wireless", "price": 349.99, "image": "https://i5.walmartimages.com/seo/Beats-Studio3-Wireless-Noise-Cancelling-Headphones-with-Apple-W1-Headphone-Chip-Defiant-Black-Red_61f9e0aa-b519-4ad1-95f5-8a8265f388f7.3430280295f34ec3d87cc41fb9d73657.jpeg", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "color": "Red", "reason-to-buy": "High-performance wireless noise cancelling headphones" } }, { "id": "10", "name": "Apple iPad Mini 5th Gen", "price": 399.99, "image": "https://m.media-amazon.com/images/I/71ZZtEr4kaL.jpg", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "capacity": "64 GB", "screen size": 7.9 } }, { "id": "11", "name": "Apple iPad Air", "price": 419.99, "image": "https://fdn2.gsmarena.com/vv/pics/apple/apple-ipad-air-1.jpg", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "generation": "4th", "capacity": "64 GB" } }, { "id": "11", "name": "Apple iPad Air", "price": 419.99, "image": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS7VqdDzEX8komd9WwWvjtcpWMWfSyPovrT66RUUTgb_PY_tgdSn-eQTXYyimf07AqHf-Q&usqp=CAU", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "generation": "4th", "capacity": "64 GB" } }, { "id": "8586", "name": "Iphone 17", "price": 2555, "image": "https://mediamobile.rs/image/cache/catalog/Telefoni/Samsung/A%20serija/A17/galaxy-a17-4g-black-283x283.jpg", "description": "sbdsnj a;skdjjsakdj skdjfskdjfhdjs skjdskjd", "characteristics": { "color": "blue" } }, { "id": "518a", "name": "Apple Max Pro Air 64", "price": 1025, "image": "https://mediamobile.rs/image/cache/catalog/Telefoni/Samsung/A%20serija/A17/galaxy-a17-4g-black-283x283.jpg", "description": "lkdsjksjf dsjishfuhs jdnsufsubfudb dishuhaihdi jadbaubuah ajhduahia", "characteristics": {} }, { "id": "3d39", "name": "Apple Max Pro Air 64", "price": 2525, "image": "https://mediamobile.rs/image/cache/catalog/Telefoni/Samsung/A%20serija/A17/galaxy-a17-4g-black-283x283.jpg", "description": "fdfgsdfghgdfghfdsa dfggd asdsd asv ASDSDSSD fgrfg", "characteristics": { "kjhgfgh": "hhhgh" } } ], "users": [] }
7
« on: 09 November 2025, 22:14:33 »
8
« on: 09 November 2025, 22:11:09 »
9
« on: 09 November 2025, 22:10:31 »
10
« on: 09 November 2025, 22:08:43 »
Spoiler for products JSON :
{ "products": [ { "id": "1", "name": "Google Pixel 6 Pro", "price": 899.99, "image": "https://amateurphotographer.com/wp-content/uploads/sites/7/2021/11/pixel-6-pro-rear-P1088727_cropped.jpg?w=435&h=350&crop=1", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "color": "Cloudy White", "capacity": "128 GB" } }, { "id": "2", "name": "Apple iPhone 12 Mini, 256GB, Blue", "price": 699.99, "image": "https://www.tehnomedia.rs/image/71882.jpg?tip=webp&tip_slike=0", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": null }, { "id": "3", "name": "Apple iPhone 12 Pro Max", "price": 1099.99, "image": "https://www.dxomark.com/wp-content/uploads/medias/post-61584/iphone-12-pro-max-graphite-hero.jpg", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "color": "Cloudy White", "capacity": "512 GB" } }, { "id": "4", "name": "Apple iPhone 11, 64GB", "price": 389.99, "image": "https://www.winwin.rs/media/catalog/product/cache/aa01455892d233ac3aea63acd876143d/1/1/1131867_000000000001131867-main-webp-580-conversion-media-format_1.webp", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "color": "Purple" } }, { "id": "5", "name": "Samsung Galaxy Z Fold2", "price": 689.99, "image": "https://images.samsung.com/is/image/samsung/p5/au/smartphones/galaxy-z-fold2/buy/002_Z-Fold2-MysticBlack-dynamic-GalleryImage-MO-img80.jpg?$ORIGIN_JPG$?imbypass=true", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "color": "Brown" } }, { "id": "6", "name": "Apple AirPods", "price": 120, "image": "https://www.apple.com/newsroom/images/tile-images/Apple-AirPods-world-most-popular-wireless-headphones_03202019.jpg.landing-big_2x.jpg", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "generation": "3rd" } }, { "id": "7", "name": "Apple MacBook Pro 16", "price": 1849.99, "image": "https://techpoets.com/img-product/428/apple-macbook-pro-16-m1-pro-1tb.jpg", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "year": 2019, "cpu model": "Intel Core i9", "hard disk size": "1 TB" } }, { "id": "8", "name": "Apple Watch Series 8", "price": 399.99, "image": "https://cdn.mos.cms.futurecdn.net/mfJNvNwN8CjPvLF7H8fSnQ.jpg", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "strap Colour": "Elderberry", "case Size": "41mm" } }, { "id": "9", "name": "Beats Studio3 Wireless", "price": 349.99, "image": "https://i5.walmartimages.com/seo/Beats-Studio3-Wireless-Noise-Cancelling-Headphones-with-Apple-W1-Headphone-Chip-Defiant-Black-Red_61f9e0aa-b519-4ad1-95f5-8a8265f388f7.3430280295f34ec3d87cc41fb9d73657.jpeg", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "color": "Red", "reason-to-buy": "High-performance wireless noise cancelling headphones" } }, { "id": "10", "name": "Apple iPad Mini 5th Gen", "price": 399.99, "image": "https://m.media-amazon.com/images/I/71ZZtEr4kaL.jpg", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "capacity": "64 GB", "screen size": 7.9 } }, { "id": "11", "name": "Apple iPad Air", "price": 419.99, "image": "https://fdn2.gsmarena.com/vv/pics/apple/apple-ipad-air-1.jpg", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "generation": "4th", "capacity": "64 GB" } }, { "id": "11", "name": "Apple iPad Air", "price": 419.99, "image": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS7VqdDzEX8komd9WwWvjtcpWMWfSyPovrT66RUUTgb_PY_tgdSn-eQTXYyimf07AqHf-Q&usqp=CAU", "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis commodi laborum rem. Temporibus sequi sunt accusantium reprehenderit doloremque accusamus error consectetur? Consectetur voluptatibus sed nihil repellendus explicabo consequatur nobis vel?", "characteristics": { "generation": "4th", "capacity": "64 GB" } } ] }
11
« on: 09 November 2025, 22:07:12 »
12
« on: 09 November 2025, 22:06:11 »
13
« on: 09 November 2025, 22:02:00 »
Spoiler for CSS :
* { box-sizing: border-box; } body { margin: 0; position: relative; min-height: 100vh; width: 100%; background-color: rgb(245, 255, 253); padding-bottom: 5rem; } body header { padding: 1rem; background-color: rgb(2, 130, 100); display: flex; justify-content: center; align-items: center; margin-bottom: 1rem; } body header h1 { margin: 0; color: white; } body main { display: flex; justify-content: center; align-items: center; flex-direction: column; padding: 1rem; } body main .search-section { padding: 1rem; margin-bottom: 1rem; width: 100%; text-align: center; } body main .search-section input { padding: 0.5rem; border-radius: 5px; box-shadow: 0 0 10px rgb(2, 130, 100); font-size: 1.1rem; width: 90%; } body .container-section { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; flex-wrap: wrap; gap: 1.5rem; } body .container-section .product { border: 1px solid rgb(2, 130, 100); border-radius: 10px; width: 25%; min-width: 350px; box-shadow: 0 0 10px rgb(2, 130, 100); color: rgb(1, 73, 56); } body .container-section .product .product-img { width: 100%; max-height: 300px; } body .container-section .product .product-info { padding: 0.5rem; display: flex; justify-content: center; align-items: center; align-content: center; flex-direction: column; } body .container-section .product .product-info .product-name { margin: 0; } body .container-section .product .product-info .product-price { margin: 0.5rem; } body .container-section .product .product-info p { margin: 0.5rem; } body .container-section .product:hover { transform: scale(1.03); } body footer { position: absolute; bottom: 0; background-color: rgb(2, 130, 100); width: 100%; } body footer p { color: white; display: flex; justify-content: center; align-items: center; text-transform: uppercase; font-weight: 600; } body .btn-add { position: fixed; bottom: 5rem; right: 3rem; width: 80px; height: 80px; border-radius: 50%; background-color: rgb(158, 2, 2); } body .btn-add div { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; } body .btn-add svg { width: 70%; height: 70%; } body .container-modal-body { text-align: center; } body .container-modal-body #name, body .container-modal-body #price, body .container-modal-body #image, body .container-modal-body #description { box-sizing: border-box; width: 90%; font-size: 1.2rem; } body .container-modal-body .characteristics { margin: 1rem 0; } body .container-modal-body .characteristics input { width: 40%; }/*# sourceMappingURL=style.css.map */
Spoiler for SCSS :
$font-color: white; $default-color: rgb(2, 130, 100); @mixin centar() { display: flex; justify-content: center; align-items: center; } * { box-sizing: border-box; } body { margin: 0; position: relative; min-height: 100vh; width: 100%; background-color: rgb(245, 255, 253); padding-bottom: 5rem; header { padding: 1rem; background-color: $default-color; @include centar(); margin-bottom: 1rem; h1 { margin: 0; color: $font-color; } } main { @include centar(); flex-direction: column; padding: 1rem; .search-section { padding: 1rem; margin-bottom: 1rem; width: 100%; text-align: center; input { padding: 0.5rem; border-radius: 5px; box-shadow: 0 0 10px $default-color; font-size: 1.1rem; width: 90%; } } } .container-section { width: 100%; height: 100%; @include centar(); flex-wrap: wrap; gap: 1.5rem; .product { border: 1px solid $default-color; border-radius: 10px; width: 25%; min-width: 350px; box-shadow: 0 0 10px $default-color; color: rgb(1, 73, 56); .product-img { width: 100%; max-height: 300px; } .product-info { padding: 0.5rem; @include centar(); align-content: center; flex-direction: column; .product-name { margin: 0; } .product-price { margin: 0.5rem; } p { margin: 0.5rem; } } &:hover { transform: scale(1.03); } } } footer { position: absolute; bottom: 0; background-color: $default-color; width: 100%; p { color: $font-color; @include centar(); text-transform: uppercase; font-weight: 600; } } .btn-add { position: fixed; bottom: 5rem; right: 3rem; width: 80px; height: 80px; border-radius: 50%; background-color: rgb(158, 2, 2); div { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; } svg { width: 70%; height: 70%; } } .container-modal-body { text-align: center; #name, #price, #image, #description { box-sizing: border-box; width: 90%; font-size: 1.2rem; } .characteristics { margin: 1rem 0; input { width: 40%; } } } }
14
« on: 09 November 2025, 22:01:10 »
15
« on: 09 November 2025, 21:57:25 »
Spoiler for main JS :
const searchInput = document.getElementById("country-name"); const searchButton = document.getElementById("search-country"); const countries = document.getElementsByClassName("countries-container")[0]; const cardElement = document.getElementsByClassName("card")[0]; function getData(countryName) { fetch(`https://restcountries.com/v3.1/name/${countryName}`) .then(function (response) { console.log(response); console.log(typeof response); if (!response.ok) throw new Error(`Country not found, status:${response.status}`); return response.json(); }) .then(function (result) { console.log(result[0]); renderCountry(result[0]); const neigbourCounty = result[0].borders ? result[0].borders[0] : null; console.log(neigbourCounty); if (!neigbourCounty) throw new Error("Neighbour country do not exist! "); return fetch(`https://restcountries.com/v3.1/alpha/${neigbourCounty}`); }) .then(function (resolve) { return resolve.json(); }) .then(function (data) { console.log(data[0]); renderCountry(data[0], "card-neigbour"); }) .catch(function (error) { renderError("Something went wrong!", error.message); }); } function renderCountry(item, classCard = "") { const language = Object.values(item.languages)[0]; console.log(language); const currency = Object.values(item.currencies)[0]; currency.template = `${currency.name},\n symbol: ${currency.symbol}`; console.log(currency.template); const cardEl = ` <div class="card ${classCard}"> <img class="card-img" src="${item.flags.png}" alt="${ item.flags.alt }" /> <div class="card-info"> <div class="card-main-info"> <h2>${item.name.common}</h2> <h4>Capital: ${item.capital[0]}</h4> </div> <div class="info"> <p>Region: ${item.region}</p> <p>Population: ${(+item.population / 1000000).toFixed(2)} mil</p> <p>Language: ${language}</p> <p>Currency: ${currency.template}</p> </div> </div> </div>`; countries.insertAdjacentHTML("beforeend", cardEl); } searchInput.addEventListener("keyup", function (event) { if (event.key === "Enter") { handlerFunction(); } }); searchButton.addEventListener("click", handlerFunction); function resetInput() { searchInput.value = ""; } function handlerFunction() { if (searchInput.value.trim()) { getData(searchInput.value); resetInput(); } else { alert("Unesite vrednot u input element!"); } } function renderError(message, errMsg) { countries.innerHTML += `${message} -> ${errMsg}`; } //II primer - renderovanje drzava po regionu function getDataI(region) { fetch(`https://restcountries.com/v3.1/region/${region}`) .then((response) => { if (!response.ok) throw new Error(`Country not found, status:${response.status}`); return response.json(); }) .then((result) => { console.log(result); for (const state of result) { renderCountry(state); } }) .catch((error) => { renderError("Something went wrong!", error.message); }); } // getDataI("asia");