<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: jiyongk84</title>
    <description>The latest articles on Forem by jiyongk84 (@jiyongk84).</description>
    <link>https://forem.com/jiyongk84</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1082828%2Fb9149dfb-6504-4495-8c33-06215804bc8c.png</url>
      <title>Forem: jiyongk84</title>
      <link>https://forem.com/jiyongk84</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jiyongk84"/>
    <language>en</language>
    <item>
      <title>Airline booking app</title>
      <dc:creator>jiyongk84</dc:creator>
      <pubDate>Mon, 09 Oct 2023 03:42:12 +0000</pubDate>
      <link>https://forem.com/jiyongk84/airline-booking-app-2lm8</link>
      <guid>https://forem.com/jiyongk84/airline-booking-app-2lm8</guid>
      <description>&lt;p&gt;In this blog post, we will explore a project that combines the frontend capabilities of React with the Flask backend, powered by SQLAlchemy for managing database models. This combination allows us to create a practical flight booking app, and we will break down some key components.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Front-End Flight Booking with React&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Flight Search&lt;br&gt;
Our flight booking app begins with a user interface powered by React. The core of the app is the flight search feature, allowing users to search for flights by entering a city name. The app fetches flight data from the server based on the city they provided.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState, useEffect } from 'react';

function SearchFlight() {
  // ...
  const handleFlightSearch = (e) =&amp;gt; {
    e.preventDefault();

    if (!cityName) {
      alert('Please enter a city name for flight search.');
      return;
    }

    setLoadingFlights(true);

    // Make a POST request to search for flights based on the city name
    fetch('/api/flights/search', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ city: cityName }),
    })
      .then((response) =&amp;gt; {
        if (response.ok) {
          return response.json();
        } else {
          throw an Error('Failed to get flight data');
        }
      })
      .then((data) =&amp;gt; {
        setFlights(data);
        setLoadingFlights(false);
      })
      .catch((error) =&amp;gt; {
        console.error('Error while fetching flight data:', error);
        setLoadingFlights(false);
      });
  }

  // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Managing the Cart&lt;/p&gt;

&lt;p&gt;Users can add and remove flights in their cart. The cart state is managed within the React app, allowing users to interact with it easily.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Define the addToCart function to add flights to the cart
const addToCart = (flight) =&amp;gt; {
  // Check if the flight is already in the cart
  if (!cart.some((item) =&amp;gt; item.id === flight.id)) {
    // If not, add it to the cart
    setCart([...cart, flight]);
  }
};

// Define the removeFromCart function to remove flights from the cart
const removeFromCart = (flightId) =&amp;gt; {
  const updatedCart = cart.filter((item) =&amp;gt; item.id !== flightId);
  setCart(updatedCart);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Booking a Flight&lt;/p&gt;

&lt;p&gt;When users are ready to book their selected flights, they can proceed to the booking step. This initiates the booking process, where we create booking objects based on the user's cart and submit these bookings to the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const handleSubmit = async () =&amp;gt; {
  if (flights.length is 0) {
    alert('Cart is empty. Add flights to the cart before submitting.');
    return;
  }

  try {
    setSubmitting(true);

    // Create an array of booking objects based on cart data
    const bookings = flights.map((cartItem) =&amp;gt; ({
      user_id: userId,
      flight_id: cartItem.id,
      order_number: generateOrderNumber(),
      order_status: 0,
    }));

    // Make a POST request to create the bookings
    const bookingResponse = await fetch('/api/bookings', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(bookings),
    });

    if (bookingResponse.ok) {
      alert('Booking successfully submitted!');
    } else {
      // Handle error
      // ...
    }
  } catch (error) {
    console.error('An error occurred:', error);
    alert('An error occurred while submitting the booking.');
  } finally {
    setSubmitting(false);
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Back-End with Flask-SQLAlchemy
&lt;/h2&gt;

&lt;p&gt;On the server side, Flask and SQLAlchemy play a significant role. Let's take a closer look at some of the essential backend models that power our flight booking app.&lt;/p&gt;

&lt;p&gt;User Model&lt;/p&gt;

&lt;p&gt;The User model represents app users and includes fields such as username, email, first name, and last name. SQLAlchemy is used to hash and manage user passwords securely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class User(db.Model):
    # ...
Airport and Flight Models
We define models for airports and flights to manage airport data and flight details. These models are interconnected through foreign keys, creating a seamless relationship.

class Airport(db.Model):
    # ...

class Flight(db.Model):
    # ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1cf8wW88--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a857eanys4mfwke06bxx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1cf8wW88--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a857eanys4mfwke06bxx.png" alt="flights table" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tQHiGZbd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/udehnnzf85rszu6bv11n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tQHiGZbd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/udehnnzf85rszu6bv11n.png" alt="users table" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Booking and Payment Models&lt;/p&gt;

&lt;p&gt;For bookings and payments, we use SQLAlchemy to manage order details. The Booking model is connected to both users and flights, linking the booking to a specific user and flight.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Booking(db.Model):
    # ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YOMZrWbK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p4lht5ko3pj934r72dng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YOMZrWbK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p4lht5ko3pj934r72dng.png" alt="Booking table" width="547" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;User Access Management&lt;/p&gt;

&lt;p&gt;User access is a crucial aspect of the flight booking app. Users can sign in, sign up, and sign out as needed. The UserAccess component in React manages this functionality.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState } from 'react';

function UserAccess({ onSignIn, onSignUp }) {
  // ...
  const handleSignIn = async () =&amp;gt; {
    if (!username || !password) {
      setInputError(true);
      return;
    }
    // ...
  };

  const handleSignUp = async () =&amp;gt; {
    if (!username || !password || !firstName || !lastName || !email) {
      setInputError(true);
      return;
    }
    // ...
  };

  const handleSignOut = async () =&amp;gt; {
    // ...
  };

  return (
    &amp;lt;div className="user-access"&amp;gt;
      {/* User sign-in and sign-up components */}
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f6YcePSz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dkxh0xh9gllhyu7ds5nb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f6YcePSz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dkxh0xh9gllhyu7ds5nb.png" alt="user sign up" width="587" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sX_LXVbt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggfxbm3lwc2v6r8dg7jk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sX_LXVbt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggfxbm3lwc2v6r8dg7jk.png" alt="user sign in" width="547" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Python backend routing(views):&lt;/p&gt;

&lt;p&gt;Here is the breakdown of the app.py to receive the request from the react front end:&lt;/p&gt;

&lt;p&gt;Find Airports (AirportSearch Class): When you're planning a trip and want to know which airports are available in your destination city, you use the "AirportSearch" feature. The app sends a request to the /api/airports/airportsearch route, which is handled by the "AirportSearch" class. This class retrieves a list of airports in your desired city and sends it back to your app.&lt;/p&gt;

&lt;p&gt;Your Info (UserProfile Class): If you want to see your personal details stored in the app, you access the "Your Info" section. The app checks your identity and then uses the /api/users/profile route, handled by the "UserProfile" class, to fetch and display your info. It's like opening your profile page.&lt;/p&gt;

&lt;p&gt;Join Us (Signup Class): If you're new to the app and want to create an account, you click on the "Join Us" or "Sign Up" button. This action triggers the app to send your registration details to the /api/users/signup route, managed by the "Signup" class. It's like filling out a form to become a member.&lt;/p&gt;

&lt;p&gt;Who's Here (CheckSession Class): You can use the "Who's Here?" feature to see who else is currently using the app. The app queries the /api/users/checksession route, which is handled by the "CheckSession" class, to find out who's logged in and active in the app.&lt;/p&gt;

&lt;p&gt;Sign In (Login Class): If you already have an account and want to log in, you click on "Sign In." The app sends your username and password to the /api/users/login route, which is managed by the "Login" class. This class checks if the provided credentials match and grants you access if they do.&lt;/p&gt;

&lt;p&gt;Sign Out (Logout Class): When you're done using the app, you click "Sign Out" to log out. This action sends a request to the /api/users/logout route, managed by the "Logout" class, which locks the door to your account and keeps it safe.&lt;/p&gt;

&lt;p&gt;Find Flights (FlightSearch Class): After selecting your destination, you use the "Find Flights" feature to find the perfect flight. The app queries the /api/flights/search route, which is handled by the "FlightSearch" class. This class retrieves and displays a list of flights departing from or arriving at your chosen location.&lt;/p&gt;

&lt;p&gt;Book a Flight (BookingFlight Class): Once you've found the ideal flight, you're eager to book it. The "Book a Flight" feature uses the /api/bookings route, which is managed by the "BookingFlight" class, to make the booking official. It's like telling the app, "I'm ready to go on this journey."&lt;/p&gt;

&lt;p&gt;These route-handler classes are like specialized workers in your app, each responsible for specific tasks, and they interact with the routes to make your app work smoothly. As a user, you interact with the features provided, and the app takes care of the rest by using the corresponding class to make it all happen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G6LfNbK4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pud8dt3akur1zlii9zcz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G6LfNbK4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pud8dt3akur1zlii9zcz.png" alt="Image description" width="776" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this blog post, we've explored how to create a practical flight booking app by combining React and Flask-SQLAlchemy. We've walked through key components, such as flight search, cart management, booking, and user access. With this information, you're all set to start building your own web applications using React and Flask. Happy coding!&lt;/p&gt;

</description>
      <category>python</category>
      <category>react</category>
      <category>flask</category>
      <category>backend</category>
    </item>
    <item>
      <title>Maintenance tech task aid CLI app with SQLAlchemy</title>
      <dc:creator>jiyongk84</dc:creator>
      <pubDate>Wed, 30 Aug 2023 20:34:05 +0000</pubDate>
      <link>https://forem.com/jiyongk84/maintenance-tech-task-aid-cli-app-with-sqlalchemy-2041</link>
      <guid>https://forem.com/jiyongk84/maintenance-tech-task-aid-cli-app-with-sqlalchemy-2041</guid>
      <description>&lt;p&gt;Hello again!&lt;/p&gt;

&lt;p&gt;On this blog post, I am going to talk about my latest project that I have worked on using Python and SQLAlchemy ver 1.4 with Alembic for database handling.&lt;/p&gt;

&lt;p&gt;What is &lt;a href="https://docs.sqlalchemy.org/en/14/index.html"&gt;SQLALchemy?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is a SQL toolkit and object-relational mapper(ORM) for the Python. It creates SQL database and can create relationships without writing SQL code. It sounds simple but it was not for me.&lt;/p&gt;

&lt;p&gt;What is &lt;a href="https://alembic.sqlalchemy.org/en/latest/"&gt;Alembic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alembic is a database migrations tool that was created by SQLAlchemy's author to help populate the database.&lt;/p&gt;

&lt;p&gt;Recap:&lt;br&gt;
SQLAlchemy: Creates a skeleton(structure) of the database.&lt;br&gt;
Alembic: Seeds or populates the data in the database.&lt;/p&gt;

&lt;p&gt;First of all, SQLAlchemy was pretty tough to understand but after many trials and errors, I managed to create a basic database using two separate JSON files and two "seed" files to create a skeleton of my database: aircraft.db based on the schema and use a "models" file to populate the actual data using Alembic.&lt;/p&gt;

&lt;p&gt;The problems that I ran into were mostly in writing the schema in the seed and models files. SQLAlchemy doesn't allow for incorrect names for database "key" references such as "id" or "user" in the schema between the tables so it gives a whole bunch of error codes.&lt;/p&gt;

&lt;p&gt;My "models" for aircraft.db&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from sqlalchemy.orm import declarative_base , relationship, backref
from sqlalchemy import Column, Integer, String, ForeignKey

Base = declarative_base()

#Aircraft fleet description
class Aircraft(Base):
    __tablename__ = 'Aircraft'

    id = Column(Integer, primary_key=True)
    make = Column(String)
    model = Column(String)
    body_type = Column(String)

    tasks = relationship("Aircraft_Tasks", backref=backref("Aircraft"))

    def __repr__(self):
        return f"\n&amp;lt;Aircraft" \
            + f"id={self.id}, " \
            + f"make={self.make}, " \
            + f"model={self.model}, " \
            + f"body_type={self.body_type} " \
            + "&amp;gt;"

#Air Transportation Association (ATA) chapter numbers and tasks 
class Aircraft_Tasks(Base):
    __tablename__ = 'Maintenance_tasks'

    id = Column(Integer, primary_key=True)
    ata_chapter_number = Column(Integer)
    ata_chapter_name = Column(String)
    task = Column(String)

    aircraft_id = Column(Integer, ForeignKey('Aircraft.id'))


    def __repr__(self):
        return f"\n&amp;lt;Aircraft_Tasks " \
            + f"id={self.id}, " \
            + f"ata_chapter_number={self.ata_chapter_number}, " \
            + f"ata_chapter_name={self.ata_chapter_name}, " \
            + f"task={self.task} " \
            + "&amp;gt;"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "&lt;strong&gt;tablename&lt;/strong&gt;" represents the name of the database table and items below such as the "id" represent the columns as shown above taking a "String" as values. The Columns can take Integers as values as defined on the second table under "ata_chapter_number"&lt;/p&gt;

&lt;p&gt;The "&lt;strong&gt;repr&lt;/strong&gt;" is a method that shows a class's object into a string readable form. It makes the class object easy to read and recreate it. &lt;/p&gt;

&lt;p&gt;After the database was created, the data was migrated using Alembic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alembic init "your directory" #initiate data migration
alembic revision --autogenerate -m 'your schema update description' #create a skeleton database file with a descriptor of your version name
alembic upgrade head #upgrade(if different) or populate/migrate the data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running "alembic upgrade head"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OYBLVvh7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vtvykr7g1d94qgt19efe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OYBLVvh7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vtvykr7g1d94qgt19efe.png" alt="SQLAlchemy database" width="295" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Populated database&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a_7T_7wa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bqifiv338nn94nyktpfm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a_7T_7wa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bqifiv338nn94nyktpfm.png" alt="SQL database display" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After creating and joining the database, I used it in my CLI app, called Aircraft tech taskAid. &lt;/p&gt;

&lt;p&gt;This app is a CLI(Command Line Interface) that basically takes user input and manages tasks by adding, deleting tasks from the database as well as adding new tasks to the database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wTjdRWXO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mhvguy3salj6pgj0w6b6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wTjdRWXO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mhvguy3salj6pgj0w6b6.png" alt="Main page" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have used Simple term Menu package by IngoMeyer441 to have a menu for the initial page. The title was created by using Text to ASCII Art Generator (TAAG) at patorjk.com and prettyCli by Noyoshi to color the different menu titles. It lists options to choose the aircraft, pending work, and quit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u_tZG8Bb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/np4uip6lz9t72q6pb1gu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u_tZG8Bb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/np4uip6lz9t72q6pb1gu.png" alt="aircraft submenu" width="467" height="199"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Main menu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#Initial app menu
    def main(self):
        while True:
            heading.hello_air_tech()
            print(green("Welcome Aircraft Tech! Please make a selection:"))
            options = ["Aircraft", "Pending work", "Quit"]
            menu = TerminalMenu(options)
            menu_entry_index = menu.show()
            # Aircraft is selected
            if menu_entry_index == 0:  
                self.show_aircraft_models()
            # Pending work is selected
            elif menu_entry_index == 1:  
                self.manage_pending_work()

            #Check if any pending tasks before exiting the app
            else:
                if self.pending_tasks:
                    print(yellow("You have pending tasks. Are you sure you want to quit? (Y/N)"))
                    choice = input().lower()
                    if choice == 'y':
                        print(magenta("Goodbye!"))
                        break
                else:
                    print(magenta("Goodbye!"))
                    break
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on the option chosen, the app will lead to another submenu such as "Pending work" as shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5WlHDrNn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ad3n2lx0knuzs2s8afe3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5WlHDrNn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ad3n2lx0knuzs2s8afe3.png" alt="Pending work submenu" width="425" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Displaying all the tasks from database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def handle_aircraft_tasks(self, selected_model):
        while True:
            print(blue(f"You have selected aircraft model: {selected_model}"))
            tasks = self.session.query(Aircraft_Tasks).all()

            if tasks:
                task_strings = [f"ATA {task.ata_chapter_number}, {task.task}" for task in tasks]
                task_menu = TerminalMenu(task_strings + ["Go Back"], title="Select a Task:")
                task_index = task_menu.show()

                if task_index &amp;gt;= 0 and task_index &amp;lt; len(tasks):
                    selected_task = tasks[task_index]
                    print(red(f"You selected: ATA {selected_task.ata_chapter_number} : {selected_task.task}"))
                    self.pending_tasks.append(selected_task)
                    print("Task added to pending work.")
                else:
                    break
            else:
                print("No tasks available for selected aircraft model.")
                break
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this section, after the aircraft is chosen, all the tasks get called by:&lt;br&gt;
&lt;code&gt;&lt;br&gt;
tasks = self.session.query(Aircraft_Tasks).all()"&lt;br&gt;
&lt;/code&gt; &lt;br&gt;
if the aircraft model was chosen by satisfying the while loop condition as True. &lt;/p&gt;

&lt;p&gt;This is the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L0-xUUck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h8zvr5r5nytgjlrwcxpf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L0-xUUck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h8zvr5r5nytgjlrwcxpf.png" alt="Tasks from database in a list format" width="632" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a task is chosen, the selected item is shown:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---oda60Wf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/58kwh1av74v4xk4rk0kf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---oda60Wf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/58kwh1av74v4xk4rk0kf.png" alt="Selected tasks" width="511" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And also added to the pending work menu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if task_index &amp;gt;= 0 and task_index &amp;lt; len(tasks):
                    selected_task = tasks[task_index]
                    print(red(f"You selected: ATA {selected_task.ata_chapter_number} : {selected_task.task}"))
                    self.pending_tasks.append(selected_task)
                    print("Task added to pending work.")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EOKnHY7o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2714tlw00m2wosotw60z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EOKnHY7o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2714tlw00m2wosotw60z.png" alt="Pending work menu" width="436" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Pending Work menu has the ability to add, delete, and create new tasks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def manage_pending_work(self):
        while True:
            if not self.pending_tasks:
                print(red("No pending tasks."))
            else:
                print("Pending tasks:")
                for i, task in enumerate(self.pending_tasks, start=1):
                    print(f"{i}. ATA {task.ata_chapter_number} : {task.task}")
            #Pending tasks submenu
            options = ["Add Task", "Remove Task", "Go Back"]
            menu = TerminalMenu(options, title="Pending Work:")
            option_index = menu.show()

            if option_index == 0:
                self.add_task_to_pending()
            elif option_index == 1:
                if not self.pending_tasks:
                    print("No pending tasks to remove.")
                else:
                    task_strings = [f"ATA {task.ata_chapter_number}, {task.task}" for task in self.pending_tasks]
                    task_menu = TerminalMenu(task_strings + ["Go Back"], title="Select a Task to Remove:")
                    task_index = task_menu.show()

                    if task_index &amp;gt;= 0 and task_index &amp;lt; len(self.pending_tasks):
                        removed_task = self.pending_tasks.pop(task_index)
                        print(f"Task removed from pending work: ATA {removed_task.ata_chapter_number} : {removed_task.task}")
                        self.remove_task_from_database(removed_task)  # Remove the task from the database
            else:
                break
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The manage_pending_work function has a condition where it will check if there are any task added the list and will "alert" the user if there are any pending tasks or not. It also has the ability to delete tasks from the database via remove_task_from_database function.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
def remove_task_from_database(self, task):&lt;br&gt;
        self.session.delete(task)&lt;br&gt;
        self.session.commit()&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
The most important part here is including session and using the delete method and commit function. Otherwise the database won't be changed and will give you a bunch of errors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I3ankQxv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1l6b3sj63dn6dzcpl6jg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I3ankQxv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1l6b3sj63dn6dzcpl6jg.png" alt="No pending tasks" width="646" height="125"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are more functions to the app but these are main ones. Thank you for reading. I hope this was helpful and see you in the next blog.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>python</category>
      <category>database</category>
      <category>development</category>
    </item>
    <item>
      <title>Point of Sale app</title>
      <dc:creator>jiyongk84</dc:creator>
      <pubDate>Mon, 03 Jul 2023 03:58:57 +0000</pubDate>
      <link>https://forem.com/jiyongk84/point-of-sale-app-1lp2</link>
      <guid>https://forem.com/jiyongk84/point-of-sale-app-1lp2</guid>
      <description>&lt;p&gt;Hello again. Let's continue on our journey to becoming a web developer. On this post, we will explore React and many of its features and applications.&lt;/p&gt;

&lt;p&gt;For this project, I have created a Point of Sale (POS) / e-commerce app. &lt;/p&gt;

&lt;p&gt;In this post, I will demonstrate the various components used in a React e-commerce application. Each component serves a specific purpose and contributes to the overall functionality of the application. I will discuss the useState and useEffect hooks, as well as the React Router, and provide an overview of how I have utilized them within each component.&lt;/p&gt;

&lt;p&gt;App Component:&lt;br&gt;
As the entry point for the application, I use the App component. It uses the React Router (BrowserRouter, Routes, and Route) to handle navigation and render different components based on the current URL. Additionally, it includes a NavBar component for navigation and manages the cartItems state using the useState hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function App() {
  const [cartItems, setCartItems] = useState([]);

  return (
    &amp;lt;Router&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;NavBar /&amp;gt;
        &amp;lt;Routes&amp;gt;
          &amp;lt;Route path="/" element={&amp;lt;Home onAddItem={setCartItems} /&amp;gt;} /&amp;gt;
          &amp;lt;Route
            path="/cart"
            element={&amp;lt;Cart items={cartItems} onDeleteItem={setCartItems} /&amp;gt;}
          /&amp;gt;
          &amp;lt;Route path="/data" element={&amp;lt;Data /&amp;gt;} /&amp;gt;
        &amp;lt;/Routes&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/Router&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NavBar Component:&lt;br&gt;
To display a simple navigation bar with links to the home page and the data page, I utilize the NavBar component. It utilizes the Link component from React Router to enable seamless navigation between different pages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JFBTBoLj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c9nzz94mqc4weplezbd7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JFBTBoLj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c9nzz94mqc4weplezbd7.png" alt="NavBar" width="800" height="23"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import { Link } from 'react-router-dom';

function NavBar() {
  return (
    &amp;lt;nav className='navbar'&amp;gt;
      &amp;lt;Link className='navbar-item' to="/"&amp;gt;Home&amp;lt;/Link&amp;gt;
      &amp;lt;span className='navbar-item'&amp;gt;|&amp;lt;/span&amp;gt;
      &amp;lt;Link className='navbar-item' to="/data"&amp;gt;Data&amp;lt;/Link&amp;gt;
    &amp;lt;/nav&amp;gt;
  );
}

export default NavBar;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Home Component:&lt;br&gt;
Representing the home page of the e-commerce application, the Home component is responsible for fetching a list of items from a server using the useEffect hook. It then displays these items as item cards. Within this component, I manage the items and cartItems states using the useState hook. Furthermore, I have integrated a Cart component to showcase the items added to the cart. Additionally, I have implemented functions to handle adding items to the cart, removing items from the cart, and clearing the cart. Finally, after the items are added, increment and decrement buttons are rendered to give additional functionality to the cart, instead of rendering multiple of the same item.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ajYx3D6K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w1ps3bi6ukuvgby370sc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ajYx3D6K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w1ps3bi6ukuvgby370sc.png" alt="Home Component" width="800" height="581"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;useEffect(() =&amp;gt; {
    fetchItems();
  }, []);

  const fetchItems = () =&amp;gt; {
    fetch('http://localhost:3000/items')
      .then((response) =&amp;gt; response.json())
      .then((data) =&amp;gt; setItems(data))
      .catch((error) =&amp;gt; console.log('Error fetching items:', error));
  };

  const handleAddToCart = (item) =&amp;gt; {
    const existingItem = cartItems.find((cartItem) =&amp;gt; cartItem.id === item.id);
    if (existingItem) {
      const updatedItems = cartItems.map((cartItem) =&amp;gt;
        cartItem.id === item.id ? { ...cartItem, quantity: cartItem.quantity + 1 } : cartItem
      );
      setCartItems(updatedItems);
      onAddItem(updatedItems);
    } else {
      setCartItems((prevItems) =&amp;gt; [...prevItems, { ...item, quantity: 1 }]);
      onAddItem({ ...item, quantity: 1 });
    }
  };

  const handleDecrementItem = (item) =&amp;gt; {
    if (item.quantity &amp;gt; 1) {
      const updatedItems = cartItems.map((cartItem) =&amp;gt;
        cartItem.id === item.id ? { ...cartItem, quantity: cartItem.quantity - 1 } : cartItem
      );
      setCartItems(updatedItems);
      onAddItem(updatedItems);
    } else {
      handleDeleteItem(item);
    }
  };

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cart Component:&lt;br&gt;
In the Cart component, my focus is on displaying the items added to the cart. By receiving the items state and various callback functions from the Home component, I can effectively manage the cartItems state using the useState hook. Within this component, I render a list of cart items, providing buttons to increment and decrement item quantities, remove items from the cart, and clear the cart. Finally, I calculate the total price of the items in the cart and display it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eyYEn_se--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t17p8p5zexbz1tefi4w1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eyYEn_se--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t17p8p5zexbz1tefi4w1.png" alt="Cart component" width="628" height="850"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
function Cart({ items, onDeleteItem, onClearItems, onAddItem, onDecrementItem }) {
  const handleRemoveItem = (index) =&amp;gt; {
    onDeleteItem(index);
  };

  const handleDecrementItem = (item) =&amp;gt; {
    onDecrementItem(item);
  }
  const handleClearItems = () =&amp;gt; {
    onClearItems();
  };

  const handleAddItem = (item) =&amp;gt; {
    onAddItem(item);
  };

  const calculateTotal = () =&amp;gt; {
    const totalPrice = items.reduce((total, item) =&amp;gt; total + item.price, 0);
    return totalPrice.toFixed(2);
  };

  return (
    &amp;lt;div className="cart-container"&amp;gt;
      &amp;lt;div className="cart-items"&amp;gt;
        {items.length === 0 ? (
          &amp;lt;p&amp;gt;Your cart is empty. Add items from the Item Selection.&amp;lt;/p&amp;gt;
        ) : (
          &amp;lt;ul&amp;gt;
            {items.map((item, index) =&amp;gt; (
              &amp;lt;li key={index}&amp;gt;
                &amp;lt;span&amp;gt;{item.name}&amp;lt;/span&amp;gt;
                &amp;lt;div&amp;gt;
                  &amp;lt;button className="decrement-item" onClick={() =&amp;gt; handleDecrementItem(item)}&amp;gt;
                    &amp;lt;FaMinus /&amp;gt;
                  &amp;lt;/button&amp;gt;
                  &amp;lt;span&amp;gt;{item.quantity}&amp;lt;/span&amp;gt;
                  &amp;lt;button className="increment-item" onClick={() =&amp;gt; handleAddItem(item)}&amp;gt;
                    &amp;lt;FaPlus /&amp;gt;
                  &amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;button className="delete-item" onClick={() =&amp;gt; handleRemoveItem(index)}&amp;gt;
                  &amp;lt;FaTrash /&amp;gt;
                &amp;lt;/button&amp;gt;
              &amp;lt;/li&amp;gt;
            ))}
          &amp;lt;/ul&amp;gt;
        )}
        {items.length &amp;gt; 0 &amp;amp;&amp;amp; (
          &amp;lt;button className="clear-button" onClick={handleClearItems}&amp;gt;
            Clear Cart
          &amp;lt;/button&amp;gt;
        )}
        &amp;lt;div className="total"&amp;gt;
          &amp;lt;span&amp;gt;Total: $ {calculateTotal()}&amp;lt;/span&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Data Component:&lt;br&gt;
The Data component acts as a page where users can both add and view items to the database. To achieve this functionality, I used controlled form inputs through the useState hook and fetch items from a server utilizing the useEffect hook. Additionally, I have incorporated a form to add items, featuring fields for name, type, picture URL, and price. When adding items, I utilize a POST request to the server, subsequently updating the allItems state. Lastly, I present a table of all items fetched from the server, allowing users to delete items individually using the delete button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5w0dxMEp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j31yuadbs5jrdv2lk4ti.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5w0dxMEp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j31yuadbs5jrdv2lk4ti.png" alt="Data component" width="800" height="278"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;useEffect(() =&amp;gt; {
    fetchItems();
  }, []);

  const fetchItems = () =&amp;gt; {
    fetch('http://localhost:3000/items')
      .then((response) =&amp;gt; response.json())
      .then((data) =&amp;gt; setAllItems(data))
      .catch((error) =&amp;gt; console.log('Error fetching items:', error));
  };

  const handleAddItem = (e) =&amp;gt; {
    e.preventDefault();

    const newItem = {
      name: itemName,
      type: itemType,
      image: itemPicture,
      price: parseFloat(itemPrice),
    };

    fetch('http://localhost:3000/items', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(newItem),
    })
      .then((response) =&amp;gt; {
        if (!response.ok) {
          throw new Error('Failed to add item');
        }
        return response.json();
      })
      .then((data) =&amp;gt; {
        onAddItem(newItem);

        setItemName('');
        setItemType('');
        setItemPicture('');
        setItemPrice('');

        fetchItems();
      })
      .catch((error) =&amp;gt; {
        console.log('Error adding item:', error);
      });
  };

  const handleDeleteItem = (id) =&amp;gt; {
    fetch(`http://localhost:3000/items/${id}`, {
      method: 'DELETE',
    })
      .then((response) =&amp;gt; {
        if (!response.ok) {
          throw new Error('Failed to delete item');
        }

        fetchItems();
      })
      .catch((error) =&amp;gt; {
        console.log('Error deleting item:', error);
      });
  };

  return (
    &amp;lt;div className="data-section"&amp;gt;
      &amp;lt;h2&amp;gt;Add Items&amp;lt;/h2&amp;gt;
      &amp;lt;form onSubmit={handleAddItem}&amp;gt;
        &amp;lt;input
          type="text"
          placeholder="Item Name"
          value={itemName}
          onChange={(e) =&amp;gt; setItemName(e.target.value)}
          required
        /&amp;gt;
        &amp;lt;input
          type="text"
          placeholder="Item Type"
          value={itemType}
          onChange={(e) =&amp;gt; setItemType(e.target.value)}
          required
        /&amp;gt;
        &amp;lt;input
          type="url"
          placeholder="Item Picture (URL)"
          value={itemPicture}
          onChange={(e) =&amp;gt; setItemPicture(e.target.value)}
          required
        /&amp;gt;
        &amp;lt;input
          type="number"
          placeholder="Item Price"
          value={itemPrice}
          onChange={(e) =&amp;gt; setItemPrice(e.target.value)}
          required
        /&amp;gt;
        &amp;lt;button type="submit"&amp;gt;Add&amp;lt;/button&amp;gt;
      &amp;lt;/form&amp;gt;

      &amp;lt;h2&amp;gt;All Items&amp;lt;/h2&amp;gt;
      &amp;lt;table className="item-table"&amp;gt;
        &amp;lt;thead&amp;gt;
          &amp;lt;tr&amp;gt;
            &amp;lt;th className="category"&amp;gt;Item Name&amp;lt;/th&amp;gt;
            &amp;lt;th className="category"&amp;gt;Item Type&amp;lt;/th&amp;gt;
            &amp;lt;th className="category"&amp;gt;Price&amp;lt;/th&amp;gt;
            &amp;lt;th className="category"&amp;gt;Actions&amp;lt;/th&amp;gt;
          &amp;lt;/tr&amp;gt;
        &amp;lt;/thead&amp;gt;
        &amp;lt;tbody&amp;gt;
          {allItems.map((item) =&amp;gt; (
            &amp;lt;tr key={item.id}&amp;gt;
              &amp;lt;td&amp;gt;{item.name}&amp;lt;/td&amp;gt;
              &amp;lt;td&amp;gt;{item.type}&amp;lt;/td&amp;gt;
              &amp;lt;td&amp;gt;{item.price}&amp;lt;/td&amp;gt;
              &amp;lt;td&amp;gt;
                &amp;lt;button onClick={() =&amp;gt; handleDeleteItem(item.id)}&amp;gt;Delete&amp;lt;/button&amp;gt;
              &amp;lt;/td&amp;gt;
            &amp;lt;/tr&amp;gt;
          ))}
        &amp;lt;/tbody&amp;gt;
      &amp;lt;/table&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This application can be used as a Point of Sale(POS) or as an e-commerce site allowing users to order or receive orders. This app can be tailored to many types of businesses such as restaurants, mechanic shops, clothing shops, and online stores.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EiF0WSNK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j8r7clqjft0bri71nih6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EiF0WSNK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j8r7clqjft0bri71nih6.png" alt="Main app" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>reactjsdevelopment</category>
      <category>css</category>
      <category>api</category>
    </item>
    <item>
      <title>My first single page app (SPA)</title>
      <dc:creator>jiyongk84</dc:creator>
      <pubDate>Mon, 15 May 2023 06:17:50 +0000</pubDate>
      <link>https://forem.com/jiyongk84/my-first-single-page-app-spa-2iib</link>
      <guid>https://forem.com/jiyongk84/my-first-single-page-app-spa-2iib</guid>
      <description>&lt;p&gt;Hello everyone. My name is Jiyong and I am a student at Flatiron. &lt;br&gt;
Welcome to my first blog post and it's a project walkthrough!&lt;/p&gt;

&lt;p&gt;This is the initial page of the app. It's called World Countries Finder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sbf-QdQl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/95wz812q71nf3q8qsao8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sbf-QdQl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/95wz812q71nf3q8qsao8.png" alt="Project webpage" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This app is a simple web page that will fetch data from an API and display a card with a given input, in this case a country name. The form will receive the data from the API and display the country's capital city, continent, the flag, and spoken language(s). The web page has a 3 files: an HTML, CSS, and JavaScript. It has a Heading, background image, and a search box. The search box was created with a form element in html and it includes the class name, button element with 'Submit' text. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OQxJzqh3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wkaiep5khh3433pggggv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OQxJzqh3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wkaiep5khh3433pggggv.png" alt="form" width="800" height="223"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uvg1oBVX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o4as6o6qdposf6z6o5dm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uvg1oBVX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o4as6o6qdposf6z6o5dm.png" alt="input form box" width="461" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The background image was implemented using CSS on the body element with background: url(path), with a height of 100vh(viewport height). Viewport height is the viewable screen's height. The value of 100vh will fill background image to the entire screen within the body element.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dd4Er9PJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/68t9dsukf0kl4jwqe9ob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dd4Er9PJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/68t9dsukf0kl4jwqe9ob.png" alt="background image" width="521" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the correct input is received, the app will create a 'card' and display it below the search box.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--35GDgfFo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6b5o6xp5zrp5xlfxoqx9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--35GDgfFo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6b5o6xp5zrp5xlfxoqx9.png" alt="card created" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The web page can fill up to 5 cards wide and will fill on the next line.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OpC_oPLu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dzb8laj4wyfkmqu95hpv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OpC_oPLu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dzb8laj4wyfkmqu95hpv.png" alt="number of cards displayed" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the country name is not found or has an incorrect spelling, it will display an alert.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eFwYxA8v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fwf1wcub9wjy8yj6j56x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eFwYxA8v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fwf1wcub9wjy8yj6j56x.png" alt="input error alert" width="800" height="161"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's the basic functionality of the single page app.&lt;/p&gt;

&lt;p&gt;But how does it all work? For that, we need to go to the JS file and Event handlers for form submissions and check out Fetch API.&lt;/p&gt;

&lt;p&gt;Event handlers basically take an event type such as a 'click', 'mouseover', or 'mouseup' and a listener, which is an object that receives a notification or a Javascript function, such as a callback function. Please visit MDN web docs for more information.&lt;br&gt;
From MDN: &lt;/p&gt;

&lt;p&gt;addEventListener(type, listener)&lt;br&gt;
addEventListener(type, listener, options)&lt;br&gt;
addEventListener(type, listener, useCapture)&lt;/p&gt;

&lt;p&gt;My app used an Event listener that 'listened' for a 'submit' event in the form and called back a function 'getCountries':&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cCs5p_RD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0tm38tannhu8a0u9jrdx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cCs5p_RD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0tm38tannhu8a0u9jrdx.png" alt="Event handler" width="800" height="197"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The getCountries() function has an 'if' statement to check if the input form is not empty denoted with if (inputValue === '') with an alert window if the condition is not met. Also it is calling the API using fetch API to receive data in JSON format after the input form condition is true. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hjqcclcH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bu2mv17bdrygpwa0ibp4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hjqcclcH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bu2mv17bdrygpwa0ibp4.png" alt="input condition check" width="800" height="103"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;fetch API&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X_H7gKsZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hfu6ow9glb1md0l86ac7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X_H7gKsZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hfu6ow9glb1md0l86ac7.png" alt="fetch API getting data" width="800" height="197"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first .then receives data and transforms in JSON format. The second .then 'implements' that JSON using .map method to create a new array of results from the JSON object and is defined with a variable 'matchingData' and it uses a callback function to return a single element in the new array. (listOneCountry(matchingData.shift()); shift taking the first element of the new array.&lt;/p&gt;

&lt;p&gt;MDN: &lt;br&gt;
map(callbackFn)&lt;br&gt;
map(callbackFn, thisArg)&lt;/p&gt;

&lt;p&gt;The second .then also has a condition: if the 'matchingData.length&amp;gt;0' run the listOneCountry() function, else display an alert 'No match found'.&lt;/p&gt;

&lt;p&gt;Finally,&lt;br&gt;
The listOneCountry() function 'creates' a card with information such as spoken languages, continent, the flag image, using different types of html elements, such as 'div', 'p', and 'img src'. Some data need to be 'extracted' using the Object.values method to get the value from the object. After that the 'card' was appended (using .appendChild) to display on the DOM. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8iwjnXDj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dl0d5vswk4mw1ioczte5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8iwjnXDj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dl0d5vswk4mw1ioczte5.png" alt="create card function" width="781" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ehpMZk5c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/anli3i2zk9cw7a68udv7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ehpMZk5c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/anli3i2zk9cw7a68udv7.png" alt="final product" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this project was helpful and there is more to come! This is not the final product!&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;p&gt;MDN web docs:&lt;br&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener"&gt;https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map"&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>css</category>
      <category>html</category>
    </item>
  </channel>
</rss>
