Kalkulator w przeglądarce z JavaScript

Pokażę jak to dzięki pomocy jednej funkcji JavaScript – eval() i podstawowej znajomości HTML, CSS i JavaScript można zbudować kalkulator w przeglądarce internetowej.

Od prostej wersji z obliczeniami w konsoli:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Calculator</title>
</head>

<body>
  <p>
    <button onclick="
      calculation +='1';
      console.log(calculation);
      ">
      1
    </button>
    <button onclick="
    calculation +='2';
    console.log(calculation);
    ">
      2
    </button>
    <button onclick="
    calculation +='3';
    console.log(calculation);
    ">
      3
    </button>
    <button onclick="
    calculation += ' + ';
    ">
      +
    </button>
  </p>
  <p>
    <button onclick="
      calculation +='4';
      console.log(calculation);
      ">
      4
    </button>
    <button onclick="
      calculation +='5';
      console.log(calculation);
      ">
      5
    </button>
    <button onclick="
      calculation +='6';
      console.log(calculation);
      ">
      6
    </button>
    <button onclick="
      calculation += ' - ';
      ">
      -
    </button>
  </p>
  <p>
    <button onclick="
      calculation +='7';
      console.log(calculation);
      ">
      7
    </button>
    <button onclick="
      calculation +='8';
      console.log(calculation);
      ">
      8
    </button>
    <button onclick="
      calculation +='9';
      console.log(calculation);
      ">
      9
    </button>
    <button onclick="
      calculation += ' * ';
      ">
      *
    </button>
  </p>
  <p>
    <button onclick="
    calculation +='0';
    console.log(calculation);
    ">
      0
    </button>
    <button onclick="
calculation += '.';
">
      .
    </button>

    <button onclick="
    calculation = eval(calculation);
    console.log(calculation);
    ">
      =
    </button>

    <button onclick="
      calculation += ' / ';
      ">
      /
    </button>
  </p>
  <p>
    <button onclick="
    calculation = '';
    console.log(0);
    ">
      Clear
    </button>
  </p>

  <script>
    let calculation = "";
  </script>
</body>

</html>

 

 

Z dodaniem domyślnych stylów i animacji z Bootstrap-a:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Calculator</title>
  <!-- Add Bootstrap CSS -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
  <style>
    .calc-button {
      width: 60px;
      height: 60px;
      margin: 5px;
    }

    .display {
      font-size: 2rem;
      margin-bottom: 20px;
      text-align: right;
      padding: 10px;
    }
  </style>
</head>

<body class="bg-light">

  <div class="container mt-5">
    <div class="row justify-content-center">
      <div class="col-md-4">
        <div class="card p-3 shadow">
          <div class="display bg-white border rounded" id="display">0</div>

          <div class="d-flex justify-content-between">
            <button class="btn btn-primary calc-button" onclick="updateCalculation('1')">1</button>
            <button class="btn btn-primary calc-button" onclick="updateCalculation('2')">2</button>
            <button class="btn btn-primary calc-button" onclick="updateCalculation('3')">3</button>
            <button class="btn btn-warning calc-button" onclick="updateCalculation(' + ')">+</button>
          </div>

          <div class="d-flex justify-content-between">
            <button class="btn btn-primary calc-button" onclick="updateCalculation('4')">4</button>
            <button class="btn btn-primary calc-button" onclick="updateCalculation('5')">5</button>
            <button class="btn btn-primary calc-button" onclick="updateCalculation('6')">6</button>
            <button class="btn btn-warning calc-button" onclick="updateCalculation(' - ')">-</button>
          </div>

          <div class="d-flex justify-content-between">
            <button class="btn btn-primary calc-button" onclick="updateCalculation('7')">7</button>
            <button class="btn btn-primary calc-button" onclick="updateCalculation('8')">8</button>
            <button class="btn btn-primary calc-button" onclick="updateCalculation('9')">9</button>
            <button class="btn btn-warning calc-button" onclick="updateCalculation(' * ')">*</button>
          </div>

          <div class="d-flex justify-content-between">
            <button class="btn btn-primary calc-button" onclick="updateCalculation('0')">0</button>
            <button class="btn btn-primary calc-button" onclick="updateCalculation('.')">.</button>
            <button class="btn btn-success calc-button" onclick="calculate()">=</button>
            <button class="btn btn-warning calc-button" onclick="updateCalculation(' / ')">/</button>
          </div>

          <div class="d-flex justify-content-center">
            <button class="btn btn-danger calc-button" onclick="clearCalculation()">Clear</button>
          </div>
        </div>
      </div>
    </div>
  </div>

  <!-- Add Bootstrap JS -->
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>

  <script>
    let calculation = '';

    function updateCalculation(value) {
      calculation += value;
      document.getElementById('display').innerText = calculation;
    }

    function calculate() {
      try {
        calculation = eval(calculation);
        document.getElementById('display').innerText = calculation;
      } catch (e) {
        document.getElementById('display').innerText = 'Error';
      }
    }

    function clearCalculation() {
      calculation = '';
      document.getElementById('display').innerText = '0';
    }
  </script>
</body>

</html>

 

Później dodaniem więcej animacji korzystając z biblioteki jQuery:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Calculator</title>

  <!-- Add Bootstrap CSS -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
  <!-- Add jQuery -->
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  <!-- Add jQuery UI for animations -->
  <link href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet">
  <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>

  <style>
    .calc-button {
      width: 70px;
      height: 70px;
      margin: 8px;
      font-size: 1.5rem;
    }

    .display {
      font-size: 2.5rem;
      margin-bottom: 20px;
      text-align: right;
      padding: 15px;
      background-color: #f8f9fa;
    }

    .container {
      margin-top: 50px;
    }

    .btn-primary,
    .btn-warning,
    .btn-danger,
    .btn-success {
      transition: transform 0.1s ease;
    }
  </style>
</head>

<body class="bg-light">

  <div class="container">
    <div class="row justify-content-center">
      <div class="col-md-4">
        <div class="card p-4 shadow-lg rounded">
          <div class="display border rounded" id="display">0</div>

          <div class="d-flex justify-content-between">
            <button class="btn btn-primary calc-button" onclick="updateCalculation('1')">1</button>
            <button class="btn btn-primary calc-button" onclick="updateCalculation('2')">2</button>
            <button class="btn btn-primary calc-button" onclick="updateCalculation('3')">3</button>
            <button class="btn btn-warning calc-button" onclick="updateCalculation(' + ')">+</button>
          </div>

          <div class="d-flex justify-content-between">
            <button class="btn btn-primary calc-button" onclick="updateCalculation('4')">4</button>
            <button class="btn btn-primary calc-button" onclick="updateCalculation('5')">5</button>
            <button class="btn btn-primary calc-button" onclick="updateCalculation('6')">6</button>
            <button class="btn btn-warning calc-button" onclick="updateCalculation(' - ')">-</button>
          </div>

          <div class="d-flex justify-content-between">
            <button class="btn btn-primary calc-button" onclick="updateCalculation('7')">7</button>
            <button class="btn btn-primary calc-button" onclick="updateCalculation('8')">8</button>
            <button class="btn btn-primary calc-button" onclick="updateCalculation('9')">9</button>
            <button class="btn btn-warning calc-button" onclick="updateCalculation(' * ')">*</button>
          </div>

          <div class="d-flex justify-content-between">
            <button class="btn btn-primary calc-button" onclick="updateCalculation('0')">0</button>
            <button class="btn btn-primary calc-button" onclick="updateCalculation('.')">.</button>
            <button class="btn btn-success calc-button" onclick="calculate()">=</button>
            <button class="btn btn-warning calc-button" onclick="updateCalculation(' / ')">/</button>
          </div>

          <div class="d-flex justify-content-center">
            <button class="btn btn-danger calc-button" onclick="clearCalculation()">Clear</button>
          </div>
        </div>
      </div>
    </div>
  </div>

  <!-- Add Bootstrap JS -->
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>

  <script>
    let calculation = '';

    function updateCalculation(value) {
      // Add animations for button press
      animateButton(value);

      calculation += value;
      $("#display").text(calculation);
    }

    function calculate() {
      try {
        calculation = eval(calculation);
        $("#display").text(calculation).effect("bounce", { times: 3 }, 500);
      } catch (e) {
        $("#display").text('Error').effect("shake", { times: 3 }, 500);
      }
    }

    function clearCalculation() {
      calculation = '';
      $("#display").text('0').fadeOut(100).fadeIn(100);
    }

    // Add animation for button clicks
    function animateButton(value) {
      $(".btn").each(function () {
        if ($(this).text() === value) {
          $(this).addClass("pressed");
          $(this).css("transform", "scale(0.95)");
          setTimeout(() => {
            $(this).css("transform", "scale(1)");
          }, 100);
        }
      });
    }

    // Animation for button press style
    $(".btn").on("mousedown", function () {
      $(this).css("transform", "scale(0.95)");
    });
    $(".btn").on("mouseup", function () {
      $(this).css("transform", "scale(1)");
    });
  </script>

</body>

</html>

 

Kończąc na kalkulatorze z motywem prosto z Matrix-a:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Matrix Calculator</title>

  <style>
    body,
    html {
      margin: 0;
      padding: 0;
      height: 100%;
      background-color: #000;
      color: #00ff00;
      font-family: 'Courier New', monospace;
      overflow: hidden;
    }

    #matrix-bg {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      z-index: 1;
      pointer-events: none;
    }

    .matrix-column {
      position: absolute;
      top: -50px;
      font-size: 14px;
      line-height: 1.5;
    }

    .container {
      position: relative;
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
      z-index: 2;
    }

    .calculator {
      background-color: rgba(0, 20, 0, 0.8);
      border: 2px solid #00ff00;
      border-radius: 10px;
      padding: 20px;
      box-shadow: 0 0 20px #00ff00;
      transition: all 0.5s ease;
    }

    .calculator:hover {
      transform: scale(1.05);
      box-shadow: 0 0 30px #00ff00;
    }

    .display {
      width: 240px;
      height: 60px;
      margin-bottom: 20px;
      text-align: right;
      padding: 15px;
      background-color: #001400;
      color: #00ff00;
      border-radius: 5px;
      border: 1px solid #00ff00;
      text-shadow: 0 0 5px #00ff00;
      transition: all 0.5s ease;
      overflow-x: scroll;
      overflow-y: hidden;
      white-space: nowrap;
      font-size: 2.5rem;
      display: flexbox;
      align-items: center;
      justify-content: flex-end;
      cursor: text;
      user-select: text;
    }

    .display-content {
      transition: all 0.3s ease;
    }

    .display.animate .display-content {
      animation: pulse 0.5s;
    }

    @keyframes pulse {
      0% {
        transform: scale(1);
      }

      50% {
        transform: scale(1.1);
      }

      100% {
        transform: scale(1);
      }
    }

    .btn {
      width: 60px;
      height: 60px;
      margin: 5px;
      font-size: 1.2rem;
      background-color: #001400;
      color: #00ff00;
      border: 1px solid #00ff00;
      border-radius: 5px;
      transition: all 0.3s ease;
      position: relative;
      overflow: hidden;
    }

    .btn::before {
      content: '';
      position: absolute;
      top: 50%;
      left: 50%;
      width: 0;
      height: 0;
      background-color: rgba(0, 255, 0, 0.5);
      border-radius: 50%;
      transform: translate(-50%, -50%);
      transition: width 0.6s ease, height 0.6s ease;
    }

    .btn:hover {
      color: #000;
      box-shadow: 0 0 15px #00ff00;
    }

    .btn:hover::before {
      width: 200%;
      height: 200%;
    }

    .btn:active {
      transform: scale(0.95);
      box-shadow: 0 0 5px #00ff00;
    }

    .btn span {
      position: relative;
      z-index: 1;
    }

    @keyframes glowPulse {
      0% {
        box-shadow: 0 0 5px #00ff00;
      }

      50% {
        box-shadow: 0 0 20px #00ff00;
      }

      100% {
        box-shadow: 0 0 5px #00ff00;
      }
    }

    .btn:focus {
      outline: none;
      animation: glowPulse 1s infinite;
    }

    .display:focus {
      outline: none;
      box-shadow: 0 0 15px #00ff00;
    }
  </style>
</head>

<body>
  <div id="matrix-bg"></div>

  <div class="container">
    <div class="calculator">
      <div class="display" id="display">
        <div class="display-content">0</div>
      </div>
      <div>
        <button class="btn" data-value="7"><span>7</span></button>
        <button class="btn" data-value="8"><span>8</span></button>
        <button class="btn" data-value="9"><span>9</span></button>
        <button class="btn" data-value="+"><span>+</span></button>
      </div>
      <div>
        <button class="btn" data-value="4"><span>4</span></button>
        <button class="btn" data-value="5"><span>5</span></button>
        <button class="btn" data-value="6"><span>6</span></button>
        <button class="btn" data-value="-"><span>-</span></button>
      </div>
      <div>
        <button class="btn" data-value="1"><span>1</span></button>
        <button class="btn" data-value="2"><span>2</span></button>
        <button class="btn" data-value="3"><span>3</span></button>
        <button class="btn" data-value="*"><span>*</span></button>
      </div>
      <div>
        <button class="btn" data-value="0"><span>0</span></button>
        <button class="btn" data-value="."><span>.</span></button>
        <button class="btn" data-value="="><span>=</span></button>
        <button class="btn" data-value="/"><span>/</span></button>
      </div>
      <div>
        <button class="btn" data-value="clear"><span>Clear</span></button>
      </div>
    </div>
  </div>

  <script>
    const matrixBg = document.getElementById('matrix-bg');
    const display = document.getElementById('display');
    const displayContent = display.querySelector('.display-content');
    const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#$%^&*()_+-=[]{}|;:,.<>?';
    const rows = Math.floor((window.innerHeight + 50) / 20);
    let calculation = '';
    let isDisplayFocused = false;

    function createColumn() {
      const column = document.createElement('div');
      column.className = 'matrix-column';
      column.style.left = `${Math.random() * window.innerWidth}px`;
      let y = -50;
      let output = '';
      for (let j = 0; j < rows; j++) {
        output += characters[Math.floor(Math.random() * characters.length)] + '<br><br>';
      }
      column.innerHTML = output;
      matrixBg.appendChild(column);

      function updateColumn() {
        y += 5;
        if (y > window.innerHeight) {
          matrixBg.removeChild(column);
        } else {
          column.style.transform = `translateY(${y}px)`;
          requestAnimationFrame(updateColumn);
        }
      }
      requestAnimationFrame(updateColumn);
    }

    function startMatrixEffect() {
      createColumn();
      setTimeout(startMatrixEffect, Math.random() * 500);
    }

    for (let i = 0; i < 5; i++) {
      setTimeout(startMatrixEffect, i * 200);
    }

    function updateDisplay() {
      const scrollPosition = display.scrollLeft;
      displayContent.textContent = calculation || '0';
      display.classList.add('animate');
      setTimeout(() => display.classList.remove('animate'), 500);

      if (!isDisplayFocused) {
        display.scrollLeft = display.scrollWidth;
      } else {
        display.scrollLeft = scrollPosition;
      }
    }

    function handleButtonClick(e) {
      const btn = e.target.closest('.btn');
      if (!btn) return;

      btn.classList.add('active');
      setTimeout(() => btn.classList.remove('active'), 200);

      const value = btn.dataset.value;
      if (value === '=') {
        try {
          calculation = eval(calculation).toString();
        } catch (error) {
          calculation = 'Error';
        }
      } else if (value === 'clear') {
        calculation = '';
      } else {
        calculation += value;
      }
      updateDisplay();
    }

    document.querySelector('.calculator').addEventListener('click', handleButtonClick);

    // Make the display focusable
    display.tabIndex = 0;

    // Track focus state
    display.addEventListener('focus', () => {
      isDisplayFocused = true;
    });

    display.addEventListener('blur', () => {
      isDisplayFocused = false;
    });

    // Prevent default calculator behavior when interacting with the display
    display.addEventListener('mousedown', (e) => {
      e.stopPropagation();
    });

    display.addEventListener('touchstart', (e) => {
      e.stopPropagation();
    });

    // Prevent keyboard input on the display
    display.addEventListener('keydown', (e) => {
      e.preventDefault();
    });

    let lastTime = 0;
    function animate(currentTime) {
      if (currentTime - lastTime > 500) {
        createColumn();
        lastTime = currentTime;
      }
      requestAnimationFrame(animate);
    }
    requestAnimationFrame(animate);
  </script>
</body>

</html>

 

Tutaj link do repozytorium z wszystkimi wersjami aplikacji: kalkulator-github

 

Oraz proste demo z działania:

 

Kodowanie ze sztuczną inteligencją – Cursor AI

Zamieszczam poniżej informacje na temat ciekawego zintegrowanego środowiska programistycznego powiązanego ze znaną już chyba każdemu sztuczną inteligencją AI (Artificial Intelligence). Nazwa programu – Cursor.
Link do oficjalnej strony projektu:
https://www.cursor.com/

I jeszcze krótkie ciekawe demo z jego możliwości:

Co ciekawe, jeżeli ktoś dotychczas korzystał z Visual Studio Code, to umożliwia on zaimportowanie z niego wtyczek, zależności/ ustawień.

Formularz kontaktowy powiązany z Google Gmail SMTP

Pokażę jak można stworzyć prosty formularz kontaktowy na naszej stronie domowej obsługujący SMTP Google Gmaila, PHPMailer bazujący na hostingu lub początkowo na własnym serwerze Apache z obsługą PHP.
Czyli, formularz kontaktowy, który stworzymy będzie przy pomocy PHPMailer-a wysyłał nam na konto Google Gmail dane z tego formularza w postaci nowych emaili.

Zanim opiszę kolejne kroki wykonania, krótka finalna prezentacja.

Oraz wygląd wysłanej wiadomości:

To już wiemy czego możemy oczekiwać, a teraz środowisko w którym będziemy pracować.
Wszystko będzie postawione na serwerze Apache z PHP. Skorzystać można z gotowego zestawu XAMPP lub też samemu oddzielnie postawić serwer z PHP.

Dobrze, teraz tworzymy jakiś katalog w folderze publicznym www, a wewnątrz plik index.html, css, js oraz konfigurujemy w utworzonym pliku html formularz kontaktowy. Tu z polami: name, email, message. Można dodawać inne lub też dowolnie konfigurować.
Załączam plik webowy:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Contact form</title>
    <!-- Bootstrap CSS -->
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
    <!-- Custom CSS -->
    <link href="styles.css" rel="stylesheet">
</head>
<body>

    
<!-- Contact Section -->
<section id="contact" class="bg-light py-5">
    <div class="container">
        <h2 class="text-center mb-4">Contact</h2>
        <form action="contact.php" method="post">
          <div class="form-group">
              <label for="name">Name</label>
              <input type="text" class="form-control" id="name" name="name" placeholder="Your Name" required>
          </div>
          <div class="form-group">
              <label for="email">Email address</label>
              <input type="email" class="form-control" id="email" name="email" placeholder="Your Email" required>
          </div>
          <div class="form-group">
              <label for="message">Message</label>
              <textarea class="form-control" id="message" name="message" rows="3" placeholder="Your Message" required></textarea>
          </div>
          <button type="submit" class="btn btn-primary">Send</button>
      </form>
      
    </div>
</section>

<!-- Bootstrap and JavaScript -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script src="scripts.js"></script>
</body>
</html>

W znajdującym się obecnie katalogu instalujemy composer z linii poleceń:

sudo apt get install composer

W końcowym rezultacie utworzony plik composer.json musi mieć odpowiednio taką zawartość jak poniżej:

{
    "require": {
        "phpmailer/phpmailer": "^6.9",
        "google/apiclient": "^2.17",
        "league/oauth2-client": "^2.7",
        "league/oauth2-google": "^4.0"
    }
}

Teraz tworzymy poprzez konto Gmail nowy Google Cloud Project.
I tak:
stan publikacji – ustawiamy na wersję produkcyjną
Musimy pamiętać, żeby zaznaczyć dane logowania OAuth 2.0 jako Web application
URI przekierowania powinny być następujące:
-http://localhost/callback.php
-http://127.0.0.1/callback.php
Otrzymujemy w wyniku procesu utworzenia projektu:
-identyfikator klienta
-tajny klucz klienta
-stan powinien być włączony

Powinniśmy również zainstalować:

composer require phpmailer/phpmailer

composer require google/apiclient

W celu otrzymania refresh-tokenu wywołujemy plik get_refresh_token.php:

php get_refresh_token.php

Niezbędne będzie utworzenie następujących dwóch plików php:

// get_refresh_token.php
<?php
require 'vendor/autoload.php';

$client = new Google_Client();
$client->setClientId('tu wstawiamy id klienta z cloud projektu');
$client->setClientSecret('tutaj wstawiamy tajny klucz klienta');
$client->setRedirectUri('http://localhost/callback.php');
$client->addScope('https://mail.google.com/');
$client->setAccessType('offline');
$client->setPrompt('consent');

// Generate the URL for the consent screen
$authUrl = $client->createAuthUrl();
echo "Please visit this URL to authorize the application: " . $authUrl . "\n";

// After authorization, Google will redirect to your callback URL with a code
// You need to exchange this code for tokens
echo "Enter the code you received: ";
$authCode = trim(fgets(STDIN));

// Exchange the auth code for tokens
$token = $client->fetchAccessTokenWithAuthCode($authCode);

if (isset($token['refresh_token'])) {
    echo "Refresh Token: " . $token['refresh_token'] . "\n";
} else {
    echo "No refresh token received. Make sure you've set access type to offline and prompted for consent.\n";
}
?>

Oraz:

// callback.php
<?php
require 'vendor/autoload.php';

$client = new Google_Client();
$client->setClientId('nasz identyfikator klienta');
$client->setClientSecret('tajny klucz klienta');
$client->setRedirectUri('http://localhost/callback.php');

if (isset($_GET['code'])) {
    $token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
    $client->setAccessToken($token);

    // Check if we have a refresh token
    if (isset($token['refresh_token'])) {
        $refresh_token = $token['refresh_token'];
        echo "Refresh Token: " . $refresh_token;
        // Here you would typically save this refresh token securely for future use
    } else {
        echo "No refresh token received.";
    }
} else {
    echo "No authorization code received.";
}
?>

Będzie nam jeszcze potrzebny refresh_token. A to oto sposób na jego uzyskanie:

php get_refresh_token.php

Wszystkie pliki powinny znajdować się w tej samej lokalizacji.

Teraz możemy zająć się tworzeniem głównego pliku PHP – > contact.php odpowiedzialnego za korzystanie z providera Google, PHPMailera i wysyłanie emaila na nasze konto pocztowe.

Poniżej plik contact.php:

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

require_once 'vendor/autoload.php';

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\OAuth;
use League\OAuth2\Client\Provider\Google;

// Stała konfiguracja
$config = [
    'client_id' => 'tu umieszczamy nasze id klienta',
    'client_secret' => 'tajny klucz klienta',
    'refresh_token' => 'utworzony przez nas refresh_token',
    'gmail_address' => 'nasza nazwa konta gmail'
];

// Sprawdź, czy dane formularza zostały przesłane
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    // Pobierz dane z formularza
    $sender_name = $_POST['name'] ?? '';
    $sender_email = $_POST['email'] ?? '';
    $message = $_POST['message'] ?? '';

    $mail = new PHPMailer(true);

    try {
        // Konfiguracja OAuth2
        $provider = new Google([
            'clientId' => $config['client_id'],
            'clientSecret' => $config['client_secret'],
        ]);

        // Wyłącz wyświetlanie logów SMTP
        $mail->SMTPDebug = SMTP::DEBUG_OFF;
        $mail->isSMTP();
        $mail->Host = 'smtp.gmail.com';
        $mail->Port = 587;
        $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
        $mail->SMTPAuth = true;
        $mail->AuthType = 'XOAUTH2';

        $mail->setOAuth(
            new OAuth([
                'provider' => $provider,
                'clientId' => $config['client_id'],
                'clientSecret' => $config['client_secret'],
                'refreshToken' => $config['refresh_token'],
                'userName' => $config['gmail_address'],
            ])
        );

        // Konfiguracja wiadomości
        $mail->setFrom($config['gmail_address'], 'Contact form');
        $mail->addAddress($config['gmail_address'], 'Your name');
        $mail->Subject = "New message from $sender_name";
        $mail->Body    = "You received a new message from:\n\n";
        $mail->Body   .= "Name: $sender_name\n";
        $mail->Body   .= "Email: $sender_email\n\n";
        $mail->Body   .= "Message:\n$message";

        $mail->send();
        echo '<p style="color: green;">Your message has been sent successfully. Thank you for contacting us!</p>';
    } catch (Exception $e) {
        echo '<p style="color: red;">An error occurred while sending the message. Please try again later.</p>';
        error_log("Sending email failed: " . $mail->ErrorInfo . "\n" . $e->getMessage());
    }
} else {
    // Wyświetl formularz, jeśli nie został jeszcze przesłany
    include 'form.html';
}
?>

I na tym kończąc powinniśmy mieć rezultat jak pokazany na samym początku tego posta.

Hosting Google Firebase: deploy App

      Pokażę jak mniej więcej można skorzystać z darmowego hostingu oferowanego przez Google. Oto kolejne kroki:

  1. Najpierw powinniśmy zainstalować Node.js:
    https://nodejs.org/en

2. Sprawdzenie instalacji w cmd lub terminalu.

3. Instalacja jakiegoś edytora, np. Visual Studio Code.

 

4. Uruchomienie strony Google Firebase.

5. Stworzenie projektu i nadanie mu jakiejś nazwy.

6. Kolejno instalacja firebase w konsoli, narzędzi firebase i zalogowanie się do serwisu.

 

7. I teraz możemy zainicjalizować szablon aplikacji i deployować aplikację na hosting.

Możemy wcześniej w katalogu public zedytować plik index.html. Na przykład tak jak poniżej.

Gotową aplikację możemy podejrzeć pod udostępnionym adresem URL.

Google Firebase to nie tylko hosting. Mamy tu też dostęp m.in. do bazy danych, autentykacji i wielu innych.

Zbudowanie aplikacji bazującej na React i Firebase: CRUD

Kolejne kroki:

  • Instalacja środowiska programistycznego np. ‘Visual Studio Code‘.
  • Instalacja Node.js.
  • Weryfikacja instalacji:

node -v

npm -v

  • Tworzenie projektu React JS:

npx create-react-app todoapp

cd todoapp

code .

npm start

  • Tworzenie projektu Firebase.
  • Tworzenie bazy danych dla aplikacji:
    a)klikamy ‘Firestore database
    b)następnie: ‘Start in test mode
    c) nadanie nazwy dla kolekcji
    d) dodanie jakichś przykładowych danych
  • Integracja Firebase z projektem React

npm install firebase

  • Stworzenie pliku konfiguracyjnego ‘firebase.js‘ w katalogu źródłowym. Tutaj dodajemy konfigurację z Firebase do połączenia:
    a) klikamy w ikonkę Web ”
    b) nadanie nazwy dla naszej aplikacji
    c) rejestracja aplikacji
    d) skopiowanie ‘firabaseConfig
//zawartość pliku 'firebase.js'
import { initializeApp } from "firebase/app"

const firebaseConfig = { //tutaj trzeba uzupełnić własną konfiguracją

  apiKey: "",

  authDomain: "",

  projectId: "",

  storageBucket: "",

  messagingSenderId: "",

  appId: ""

};

export const app = initializeApp(firebaseConfig);
//App.js
import './App.css';
import { Component } from 'react';
import { collection, deleteDoc, getDocs, getFirestore, addDoc, doc, setDoc } from 'firebase/firestore/lite';
import { app } from './firebase';

class App extends Component {

  constructor(props) {
    super(props);
    this.state = {
      notes: []
    }
  }

  async refreshNotes() {
    var notesList = [];
    const db = getFirestore(app);
    const notesCol = collection(db, 'notes');
    const notesSnapshot = await getDocs(notesCol);

    notesSnapshot.forEach(doc => {
      let note = doc.data();
      note.id = doc.id;
      notesList.push(note);
    });
    this.setState({ notes: notesList });
  }

  componentDidMount() {
    this.refreshNotes();
  }

  async addClick() {
    var newNotes = document.getElementById("newNotes").value;
    var newNotesObject = { description: newNotes };
    const db = getFirestore(app);
    const notesCol = collection(db, 'notes');
    await addDoc(notesCol, newNotesObject);
    this.refreshNotes();
  }

  async deleteClick(id) {
    const db = getFirestore(app);
    const notesRef = doc(db, 'notes/' + id);

    await deleteDoc(notesRef);
    this.refreshNotes();
  }

  async updateClick(id) {
    const updatedDescription = prompt("Enter the updated description:");
    if (!updatedDescription) return; // If user cancels the prompt

    const db = getFirestore(app);
    const notesRef = doc(db, 'notes', id);

    await setDoc(notesRef, { description: updatedDescription }, { merge: true });
    this.refreshNotes();
  }

  render() {
    const { notes } = this.state;
    return (
      <div className="App">
        <h2>Todoapp</h2>
        <input id="newNotes" /> 
        <button onClick={() => this.addClick()}>Add Notes</button>
        {notes.map(note =>
          <p>
            <b>* {note.description}</b>  
            <button onClick={() => this.deleteClick(note.id)}>Delete Notes</button>
            <button onClick={() => this.updateClick(note.id)}>Update Notes</button>
          </p>
        )}
      </div>
    );
  }
}

export default App;

Opcjonalnie/dodatkowo możemy:

  1. Zainstalować wtyczkę Visual Studio Extension: ‘Prettier-Code formatter‘ do sformatowania kodu żeby bardziej przejrzyście wyglądał
    skrót: ‘Ctrl + Shift + I’ do użycia po włączeniu wtyczki
  2. Instalacja rozszerzenia ‘Print‘, jeżeli chcemy wydrukować kod źródłowy.

Tabele w HTML i animacja JavaScript

W tym wpisie zaprezentuję możliwe zastosowanie HTML, CSS i JS do strony, w której są  odpowiednio ostylowane dwie tabele i animacja przewijania strony w JS.

Środowisko programistyczne użyte do utworzenia projektu to Visual Studio Code z rozszerzeniem “Live Server“.
Po zainstalowaniu i włączeniu wtyczki klikamy prawym przyciskiem myszy na utworzonym pliku HTML, a następnie w “open with Live Server”. Powinna nam się otworzyć domyślna przeglądarka z podglądem strony webowej.

Poniżej zdjęcia poglądowe jak to mniej więcej powinno wyglądać:

Od razu umieszczę końcowy kod ze szczegółowymi komentarzami, a następnie opiszę co po kolei tworzyłem.

<!-- plik HTML -->
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="utf-8"/>
<title>Let's go start</title>
<!-- zewnętrzny arkusz stylów-->
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<h1 class="header">
    <div class="wrapper">

        <table> <!--tabela w HTML-->
            <tr> <!-- naglowek -->
                <th colspan="4">USŁUGI INTERNETOWE</th> 
             </tr>
            <tr> <!-- naglowki-->
               <th>Nr</th> <th>HOSTING</th> <th>DOMENA_1</th> <th>DOMENA_2</th>
            </tr>
            <tr> <!-- wiersz-->
               <td>1</td> <td>100</td> <td>0</td> <td>40</td> <!-- kolumny-->
            </tr>
            <tr> 
                <td>2</td> <td>100</td> <td>40</td> <td>40</td> <!-- kolumny-->
             </tr>
             <tr> 
                <td>3</td> <td>120</td> <td>50</td> <td>50</td> <!-- kolumny-->
             </tr>
             <tr id="scroll"> <!-- utworzenie id w celu umożliwienia identyfikacji do animacji-->
                <td colspan="4">przewiń w dół</td>
             </tr>
         </table>
        
       
    </div>
    <div class="wrapper">
    <table>
        <tr>
            <th colspan="3">PODSUMOWANIE USŁUG INTERNETOWYCH<br>
                            na dzień xx.xx.xxxx</th>
        </tr>
        <tr>
            <th>HOSTING</th> <th>DOMENA_1</th> <th>DOMENA_2</th>
        </tr>
        <tr>
            <td>jest do xx.xx.xxxx</td> <td>jest do xx.xx.xxxx</td> <td>jest do xx.xx.xxxx</td>
        </tr>
        <tr>
            <td colspan="3">Dotychczasowy poniesiony koszt</td>
        </tr>
        <tr>
            <td>320</td> <td>90</td> <td>130</td>
        </tr>
        <tr>
            <td colspan="2">410</td>  <td>130</td>
        </tr>
        <tr>
            <td colspan="3">540 - łącznie zł</td>
        </tr>
    </table>
    </div>
</h1>
<!-- zewnętrzny plik javascript-->
<script src="js/script.js"></script>
</body>
</html>
/* plik CSS */
body{ /* tło strony */
    background-color: rgb(79, 173, 76);
}
.header{ /*kolor tekstu*/
    color: darkblue;
}
.wrapper{ /* najczęściej stosowana metoda wyśrodkowania elemntu
            wrapper musi być rozciągnięty na całą szerokość i wysokość okna */    
    width: 100vw;
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
}

table, th, tr, td { /* utworzenie obramowania */
    border: 1px solid black;
}
th, td{ /* utworzenie odpowiedniego odstępu od obramowania i wycentrowania zawartości*/
    padding: 20px;
    text-align: center;
}
//plik JS
//pobranie elementu o id 'scroll' i w momencie jego kliknięcia wywołanie funkcji scroll()
document.getElementById("scroll").onclick = function() {scroll()};


//funkcja scroll z zainicjowana na 0 zmienną i oraz wywołaniem podfunkcji move()
function scroll(){
    i = 0;   
    move();
}


function move(){
    i++; //zwiększanie o jeden zmiennej i
    window.scrollBy(0,1); //przesuwanie ekranu w pionie
    /*jeżeli i będzie mniejsza o wysokość ekranu, wykonuj co pewien czas upłynięcia funkcję move(),
    czyli inaczej mówiąc przesuwaj w dół zawartość ekranu o całą jego wysokość względem czasu*/
    if (i < screen.height) setTimeout('move()',1);
}

Najpierw utworzyłem strukturę katalogów.
Odpowiednio folder js dla plików JS, CSS dla arkuszów styli, index.html pozostał w głównym katalogu. Oraz odpowiednio w katalogu css plik style.css, a w js script.js.

Na wstępie pisania strony tworzę typowy plik html w strukturze HTML5.
Od razu dodaje zewnętrzny arkusz styli w znaczniku head. A na końcu ciała body odwołanie do zewnętrznego skryptu JavaScript.

Początkowo mam zamiar wszystko wypośrodkować więc tworzę do tego celu div-a z nazwą klasy wrapper. Potem tak jak jest napisane w pliku arkuszów styli za pomocą .wrapper odwołuje się do tego elementu oraz go wypośrodkowuje w poziomie i pionie.

Przyszedł czas na utworzenie tabel.
Słowo kluczowe/ znacznik table odpowiada za utworzenie tabeli.
tr – to wiersz
th– nagłówek
td– podział na kolumny
Najpierw tworzymy wiersz, następnie w zależności ile chcemy kolumn tyle tworzymy nagłówków. Kolumny możemy łączyć za pomocą słowa kluczowego colspan, jako argument podajemy na ile kolumn dana kolumna ma się rozszerzyć.
Kolejno tworzymy następne wiersze i kolumny.
Czas na stylizację. Dodajemy do tabeli i wszystkiego co wewnątrz obramowanie. Stosując przecinek jak pokazane w przykładzie możemy bez multiplikowania kodu użyć do kilku elementów tego samego bloku CSS. Odpowiednio następnie dodaje odstęp w tabeli od obramowania i wypośrodkowuje tekst, żeby to w miarę estetycznie wyglądało.

Kolej na drugą tabelę. Pamiętam o tym, żeby tabela była umieszczona we wrapperze, żeby zachować odpowiednie ostylowanie co do wyśrodkowania.

I teraz końcowo dodaje identyfikator scroll do ostatniego wiersza w pierwszej tabeli w celu umożliwienia odwołania się w skrypcie do wywołania funkcji scroll po kliknięciu na wiersz w tabeli.

Plik JS to prosta głównie jedna funkcja move() odpowiedzialna za przewijanie strony w dół ekranu.

Poniżej końcowy efekt:

O

Link do repo:

https://github.com/traininguniverse/simple-table

Bezpieczne hasło

Przy wymyślaniu hasła do jakiegoś serwisu czy też usługi powinniśmy stosować się do pewnych reguł. I tak między innymi:

Utworzone hasło nie powinno być hasłem słownikowym.

Nie powinniśmy w nim korzystać z danych osobowych, np. imienia, nazwiska, daty urodzenia swojej ani kogoś z bliskich.

Dobre hasło powinno mieć minimum 8 znaków, składać się z dużych liter i małych, cyfr oraz zawierać znaki specjalne.

Do każdej usługi powinniśmy używać innego hasła.

Dobrym rozwiązaniem jest korzystanie z menedżerów haseł: jedno hasło do wielu innych.
Często taki menedżer umożliwia zabezpieczenie dodatkowym plikiem, który możemy przechowywać w bezpiecznym miejscu.

Podsumowując, bezpieczne hasło zawiera:
-minimum 8 znaków
-dużą literę
-małą literę
-cyfrę
-znak specjalny

Im więcej znaków, ich kombinacji tym hasło jest trudniejsze do rozszyfrowania.

Postacie normalne w relacyjnej bazie danych

  • 1NF (pierwsza postać normalna) – dziedziny atrybutów muszą być elementarne (nierozkładalne)
  • 2NF (druga postać normalna) – musi zachodzić 1NF i każdy atrybut niebędący kluczem musi być funkcjonalnie zależny od wszystkich części klucza głównego
    ->czyli jeżeli klucz główny składa się z kilku atrybutów, każdy inny atrybut musi być zależny od kombinacji tych atrybutów
    ->inaczej mówiąc: atrybut nie będący kluczem nie może zależeć tylko od części klucza głównego
  • 3NF (trzecia postać normalna) – musi zachodzić 2NF i żaden atrybut niebędący kluczem nie może zależeć od czegoś innego niż klucz

Postacie te mają na celu pozbycie się rendudacji, wieloznaczności, anomalii i niespójności w relacyjnej bazie danych.

Zapytania i operacje w bazie danych

W skrócie opisuję poniżej, które występują:

  • DML (Data Manipulation Language):
    -INSERT
    -UPDATE
    -DELETE
  • DDL (Data Definition Language):
    -CREATE (table, database, index)
    -DROP (table, database, index)
    -ALTER (alter table add/drop/update column)
  • DCL (Data Control Language):
    -GRANT
    -REVOKE
    -DENY
  • DQL (Data Query Language)
    -SELECT

Nowy proces w terminalu i dalsza praca

Często zdarza się, że pracując w terminalu pod linux-em uruchamiając nowy proces poza terminalem chcemy wciąż mieć możliwość korzystania z linii poleceń terminala.

Jest to możliwe dzięki użyciu znaku specjalnego – ampersanda – ‘&’, na końcu komendy. Uruchamia się wtedy jakaś aplikacja już w nowym procesie i można dalej korzystać z terminala.

Przykład:
Komendą ‘ps’ wyświetlamy uruchomione procesy:

Załóżmy teraz, że chcemy otworzyć dowolny edytor tekstowy i dalej pracować w dotychczas otwartym oknie. Domyślnie nie jest to możliwe:

Jednak, gdy użyjemy znaku ampersanda:

Możemy wówczas pracować i w nowo otwartej aplikacji niezależnie z terminalem, gdyż utworzył się oddzielny proces, co jest pokazane na obrazku powyżej.