Show Posts

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

Pages: [1] 2 3 ... 60
1
Javascript / Re: Java Script 17 Dan 28 MODAL
« 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
Javascript / Re: Java Script 17 Dan 28 MODAL
« 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
Javascript / Re: Java Script 17 Dan 28 MODAL
« on: 09 November 2025, 22:19:08 »

Spoiler for helper JS:


function truncateData(value, limit = 10) {
  if (typeof value === "string") {
    value = value.split(" ");
  }
  if (limit > value.length) {
    limit = value.length;
  }
  const noviNiz = [];
  for (let i = 0; i <= limit - 1; i++) {
    noviNiz.push(value[i]);
  }
  return noviNiz.join(" ") + "...";
}

function debaunce(callback, delay) {
  let timeoutID;
  return function () {
    if (timeoutID) clearTimeout(timeoutID);
    timeoutID = setTimeout(function () {
      callback();
    }, delay);
  };
}

function correctName(text) {
  const arrOfWord = text.toLowerCase().split(" ");
  console.log(arrOfWord);
  return arrOfWord
    .map((word) => word[0].toUpperCase() + word.slice(1))
    .join(" ");
}
export { correctName, truncateData, debaunce };


4
Javascript / Re: Java Script 17 Dan 28 MODAL
« 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
Javascript / Re: Java Script 17 Dan 28 MODAL
« 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
Javascript / Re: Java Script 17 Dan 28 MODAL
« on: 09 November 2025, 22:16:16 »

Spoiler for api JS:


async function fetchData(callback, url) {
  try {
    const response = await fetch(url);
    const result = await response.json();
    callback(result);
  } catch (err) {
    console.log(err.message);
  }
}

async function createProduct(product, url) {
  try {
    const result = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(product),
    });

    const response = await result.json();
    console.log(response);
  } catch (err) {
    console.log(err.message);
  }
}

export { fetchData, createProduct };







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
Javascript / Java Script 17 Dan 28 MODAL
« on: 09 November 2025, 22:14:33 »

Spoiler for HTML index:


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
      crossorigin="anonymous"
    />
    <script
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
      crossorigin="anonymous"
    ></script>
    <link rel="stylesheet" href="style.css" />
    <script src="main.js" type="module" defer></script>
  </head>
  <body>
    <header>
      <h1>TechPoint</h1>
    </header>
    <main>
      <section class="search-section">
        <input type="search" id="search-input" placeholder="Product name" />
      </section>

      <section class="container-section"></section>
    </main>
    <footer>
      <p>Made with JSON-server !</p>
    </footer>

    <!-- button -->
    <div class="btn-add" data-bs-toggle="modal" data-bs-target="#modal">
      <div>
        <svg
          width="30px"
          height="30px"
          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">
            <path
              d="M4 12H20M12 4V20"
              stroke="#fff"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            ></path>
          </g>
        </svg>
      </div>
    </div>

    <!-- bootstrap modal -->

    <!-- Modal -->
    <div
      class="modal fade"
      id="modal"
      tabindex="-1"
      aria-labelledby="modalLabel"
      aria-hidden="true"
    >
      <div class="modal-dialog modal-dialog-centered">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title" id="modalLabel">Create product</h5>
            <button
              type="button"
              class="btn-close"
              data-bs-dismiss="modal"
              aria-label="Close"
            ></button>
          </div>
          <div class="modal-body container-modal-body">
            <input
              type="text"
              placeholder="Name of product"
              id="name"
              minlength="3"
            />
            <br />
            <input
              type="number"
              min="0"
              max="10000"
              placeholder="Price"
              id="price"
            />
            <br />
            <input type="url" placeholder="Image url" id="image" />
            <br />
            <textarea
              name="description"
              id="description"
              placeholder="Description"
            ></textarea>
            <fieldset class="characteristics">
              <legend>Characteristics</legend>
              <div>
                <input type="text" placeholder="Key" id="characteristics-key" />
                <input
                  type="text"
                  placeholder="Value"
                  id="characteristics-value"
                />
              </div>
            </fieldset>
            <article></article>
          </div>
          <div class="modal-footer">
            <button
              type="button"
              class="btn btn-secondary"
              data-bs-dismiss="modal"
            >
              Close
            </button>
            <button type="button" class="btn btn-primary create-product-btn">
              Create product
            </button>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>



8
Javascript / Re: Java Script 16 Dan 27
« on: 09 November 2025, 22:11:09 »

Spoiler for modal 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 newProduct = {
  name: null,
  price: -1,
  image: null,
  description: null,
  characteristics: {},
};

createProductNameInput.addEventListener("blur", function (event) {
  const nameInputValue = 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;
  console.log(imageInputValue);

  if (
    (imageInputValue.startsWith("www") ||
      imageInputValue.startsWith("http://") ||
      imageInputValue.startsWith("https://")) &&
    (imageInputValue.endsWith(".jpg") ||
      imageInputValue.endsWith(".jpeg") ||
      imageInputValue.endsWith(".png"))
  ) {
    newProduct.image = imageInputValue;
    console.log(newProduct);
  } else {
    console.log("Image url doesn't have right format!");
  }
});


9
Javascript / Re: Java Script 16 Dan 27
« on: 09 November 2025, 22:10:31 »

Spoiler for api JS:


export async function fetchData(callback) {
  try {
    const response = await fetch("http://localhost:3000/products");
    const result = await response.json();
    callback(result);
  } catch (err) {
    console.log(err.message);
  }
}


10
Javascript / Re: Java Script 16 Dan 27
« 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
Javascript / Re: Java Script 16 Dan 27
« on: 09 November 2025, 22:07:12 »

Spoiler for helper JS:


export function truncateData(value, limit = 10) {
  if (typeof value === "string") {
    value = value.split(" ");
  }
  if (limit > value.length) {
    limit = value.length;
  }
  const noviNiz = [];
  for (let i = 0; i <= limit - 1; i++) {
    noviNiz.push(value[i]);
  }
  return noviNiz.join(" ") + "...";
}

export function debaunce(callback, delay) {
  //max
  let timeoutID;
  return function () {
    if (timeoutID) clearTimeout(timeoutID);
    timeoutID = setTimeout(function () {
      callback();
    }, delay);
  };
}

//closure
// function outer() {
//   let x = 1;

//   return function () {
//     console.log(x);
//   };
// }

// const res = outer();
// console.log(res);

// res();
// res();
// res();
// res();

// //II primer

function first() {
  let a = 10;

  return function () {
    if (a === 0) return a;
    a--;
    return a;
  };
}

// const closure = first();
// console.log(closure);

// console.log(closure());
// console.log(closure());
// console.log(closure());
// console.log(closure());
// console.log(closure());
// console.log(closure());
// console.log(closure());
// console.log(closure());
// console.log(closure());
// console.log(closure());
// console.log(closure());
// console.log(closure());
// console.log(closure());
// console.log(closure());

// const res2 = first();
// console.log(res2);

// console.log(res2());
// console.log(res2());
// console.log(res2());
// console.log(res2());
// console.log(res2());
// console.log(res2());
// console.log(res2());


12
Javascript / Re: Java Script 16 Dan 27
« on: 09 November 2025, 22:06:11 »

Spoiler for main JS:


import { truncateData, debaunce } from "./helper.js";
import { fetchData } from "./api.js";

const containerProducts =
  document.getElementsByClassName("container-section")[0];
const searchInput = document.getElementById("search-input");
const addBtn = document.getElementsByClassName("btn-add")[0];

function renderProducts(products) {
  products = filterData(products);
  console.log(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 array = [];
  // for (const item of data) {
  //   if (item.name.includes(searchValue)) {
  //     array.push(item);
  //   }
  // }
  // return array;
  const filteredData = data.filter((item) =>
    item.name.toLowerCase().includes(searchValue)
  );
  return filteredData;
}

function searchInputLogic() {
  fetchData(renderProducts);
}

document.addEventListener("DOMContentLoaded", function () {
  fetchData(renderProducts);
  const debaunceLogic = debaunce(searchInputLogic, 2000);
  searchInput.addEventListener("input", function (event) {
    debaunceLogic();
  });
});


13
Javascript / Re: Java Script 16 Dan 27
« 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
Javascript / Java Script 16 Dan 27 MODAL
« on: 09 November 2025, 22:01:10 »
Spoiler for HTML bootstrap modal:


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
      crossorigin="anonymous"
    />
    <script
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
      crossorigin="anonymous"
    ></script>
    <link rel="stylesheet" href="style.css" />
    <!-- <script src="main.js" type="module" defer></script> -->
    <!-- <script src="helper.js" defer></script> -->
    <script src="modal.js" defer></script>
  </head>
  <body>
    <header>
      <h1>Products</h1>
    </header>
    <main>
      <section class="search-section">
        <input type="search" id="search-input" placeholder="Product name" />
      </section>

      <section class="container-section"></section>
    </main>
    <footer>
      <p>Made with JSON-server !</p>
    </footer>

    <!-- button -->
    <div class="btn-add" data-bs-toggle="modal" data-bs-target="#modal">
      <div>
        <svg
          width="30px"
          height="30px"
          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">
            <path
              d="M4 12H20M12 4V20"
              stroke="#fff"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            ></path>
          </g>
        </svg>
      </div>
    </div>

    <!-- bootstrap modal -->

    <!-- Modal -->
    <div
      class="modal fade"
      id="modal"
      tabindex="-1"
      aria-labelledby="modalLabel"
      aria-hidden="true"
    >
      <div class="modal-dialog modal-dialog-centered">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title" id="modalLabel">Create product</h5>
            <button
              type="button"
              class="btn-close"
              data-bs-dismiss="modal"
              aria-label="Close"
            ></button>
          </div>
          <div class="modal-body container-modal-body">
            <input
              type="text"
              placeholder="Name of product"
              id="name"
              minlength="3"
            />
            <br /><br />
            <input
              type="number"
              min="0"
              max="10000"
              placeholder="Price"
              id="price"
            />
            <br /><br />
            <input type="url" placeholder="Image url" id="image" />
            <br /><br />
            <textarea
              name="description"
              id="description"
              placeholder="Description"
            ></textarea>
            <fieldset class="characteristics">
              <legend>Characteristics</legend>
              <input type="text" placeholder="Key" id="characteristics-key" />
              <input
                type="text"
                placeholder="Value"
                id="characteristics-value"
              />
            </fieldset>
          </div>
          <div class="modal-footer">
            <button
              type="button"
              class="btn btn-secondary"
              data-bs-dismiss="modal"
            >
              Close
            </button>
            <button type="button" class="btn btn-primary">Save changes</button>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>


15
Javascript / Re: Java Script 15 Dan 26 ASYNC , AWAIT, EVENT-LISTENERS
« 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");







Spoiler for async-await 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 renderCountry(item, classCard = "") {
  const language = Object.values(item.languages)[0];
  const currency = Object.values(item.currencies)[0];
  currency.template = `${currency.name},\n
   symbol: ${currency.symbol}`;
  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()) {
    fetchData(searchInput.value);
    resetInput();
  } else {
    alert("Unesite vrednot u input element!");
  }
}

function renderError(message, errMsg) {
  countries.innerHTML += `${message} -> ${errMsg}`;
}

async function fetchData(countryName) {
  try {
    const response = await fetch(
      `https://restcountries.com/v3.1/name/${countryName}`
    );
    if (!response.ok)
      throw new Error(`Country not found, status:${response.status}`);
    const result = await response.json();
    renderCountry(result[0]);
    const neigbourCountry = result[0].borders ? result[0].borders[0] : null;

    if (!neigbourCountry) throw new Error("Neighbour country don't exist!");

    const neigbourResponse = await fetch(
      `https://restcountries.com/v3.1/alpha/${neigbourCountry}`
    );
    const dataNeighbour = await neigbourResponse.json();

    renderCountry(dataNeighbour[0]);
  } catch (err) {
    renderError("Something went wrong!", err.message);
  }
}

//try-catch

// let x = 1;
// const y = 2;
// x = 3;
// // y = 4;
// try {
//   let x = 1;
//   const y = 2;
//   x = 3;
//   y = 4;
// } catch (err) {
//   console.log(err);
// }

// k = 5;
// console.log(k);


Pages: [1] 2 3 ... 60

SimplePortal 2.3.7 © 2008-2025, SimplePortal