<?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>DEV Community: Tomi Longe</title>
    <description>The latest articles on DEV Community by Tomi Longe (@tomilonge).</description>
    <link>https://dev.to/tomilonge</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2142902%2F93edf704-d1d0-48fd-bfbb-02e42aed54ba.jpeg</url>
      <title>DEV Community: Tomi Longe</title>
      <link>https://dev.to/tomilonge</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tomilonge"/>
    <language>en</language>
    <item>
      <title>What I Learnt From Working on Culver, a Real-Time Chat App</title>
      <dc:creator>Tomi Longe</dc:creator>
      <pubDate>Thu, 28 May 2026 22:59:02 +0000</pubDate>
      <link>https://dev.to/tomilonge/what-i-learnt-from-working-on-culver-a-real-time-chat-app-1mcl</link>
      <guid>https://dev.to/tomilonge/what-i-learnt-from-working-on-culver-a-real-time-chat-app-1mcl</guid>
      <description>&lt;p&gt;Culver is a real-time chat application, and I have been working mainly on the backend side of the product. My work so far has touched the database structure, user endpoints, message persistence, conversation history, Socket.io message events, message status tracking, and offline notification queueing.&lt;br&gt;
It has been a very practical experience because I was not just reading about how these things work. I was actually building them, running into issues, debugging them, and learning how the different parts of the system connect.&lt;/p&gt;

&lt;p&gt;My first major task was setting up the database layer with PostgreSQL and Knex.&lt;br&gt;
We already had a DBML schema that described the tables we needed for the app, but I had to turn that schema into actual Knex migrations. This included tables like users, devices, sessions, contacts, conversations, conversation_members, messages, message_status, attachments, and notifications.&lt;br&gt;
One of the first things I had to pay attention to was the order of the migrations. Some tables depend on others, so I could not just create them randomly. For example, the users table had to exist before tables like sessions, contacts, and messages, because those tables reference users. The same thing applied to conversations, which had to exist before conversation_members and messages.&lt;br&gt;
That helped me understand foreign key relationships better in a real project. It is one thing to know that tables can reference each other, but it is different when you are actually writing the migrations and the order starts to matter.&lt;br&gt;
I also added indexes where they were needed, especially for fields that would be queried often. One important example was messages. In a chat app, messages need to be fetched by conversation and sorted by time, so I added indexing around conversation_id and sent_at to support that.&lt;br&gt;
After the migrations, I added seed data so we could test locally with sample users, conversations, and messages. This made development easier because we were not always working with an empty database.&lt;/p&gt;

&lt;p&gt;After the tables were created, I worked on the Knex model layer.&lt;br&gt;
At first, it is tempting to just write database queries directly inside route handlers, but I started to see why that can get messy quickly. So I worked with a cleaner structure where each table had its own model file.&lt;br&gt;
For example, the user-related queries were handled in the users model, message-related queries in the messages model, conversation-related queries in the conversations model, and so on.&lt;br&gt;
This made the code easier to reason about because the routes and socket handlers did not need to know all the details of the database queries. They could just call a model method like “find this user” or “create this message.”&lt;br&gt;
That was one of the parts that helped me understand separation of concerns better. The route should not be doing everything. The socket handler should not be doing everything. Each part of the backend should have its own responsibility.&lt;/p&gt;

&lt;p&gt;I also worked on some user endpoints, including:&lt;br&gt;
GET /api/v1/users/:id&lt;br&gt;
PATCH /api/v1/users/me&lt;br&gt;
The GET /api/v1/users/:id endpoint fetches a user profile by UUID, while PATCH /api/v1/users/me allows the current user to update their profile information, such as their name, avatar URL, and bio.&lt;br&gt;
At that point, the full authentication middleware was not ready yet, so for local development, I used a temporary x-user-id header to identify the current user. That was not meant to be the final solution, but it allowed the user profile feature to move forward while the authentication part was still being worked on.&lt;br&gt;
That also taught me something about working in a team project, where I can create a temporary bridge so one part of the work does not block another. &lt;/p&gt;

&lt;p&gt;The next major part I worked on was message routing and persistence.&lt;br&gt;
I had to think about what happens when a user sends a message, how that message is saved, how other users receive it, and how the frontend can fetch old messages.&lt;br&gt;
I added support for fetching message history through:&lt;br&gt;
GET /api/v1/conversations/:id/messages&lt;br&gt;
This endpoint returns messages for a conversation, but before it does that, it checks that the authenticated user is actually a member of that conversation. That check is important because users should not be able to fetch messages from conversations they do not belong to.&lt;br&gt;
I also added pagination using a before timestamp and limit. This means the frontend does not need to load every single message in a conversation at once. It can load the latest messages first, then request older messages when the user scrolls up.&lt;br&gt;
That made me understand why pagination is so important. A chat may look small at the beginning, but over time a conversation can have hundreds or thousands of messages. If the backend always returns everything, it will eventually become slow and inefficient.&lt;/p&gt;

&lt;p&gt;I also worked on the conversation list endpoint:&lt;br&gt;
GET /api/v1/conversations&lt;br&gt;
This endpoint returns the conversations the authenticated user belongs to. &lt;br&gt;
I also added model logic to fetch each conversation with its latest message and unread count.&lt;br&gt;
This part made me appreciate how much work goes into something that looks simple on the UI. On most chat apps, the conversation list looks very basic, name, last message, time, unread count. But on the backend, you have to join the right tables, find the latest message, and calculate unread messages correctly for that specific user.&lt;/p&gt;

&lt;p&gt;After working on REST endpoints, I moved into the Socket.io side of message handling.&lt;br&gt;
I worked on the message:send event. The idea is that when a user sends a message, the client sends an encrypted payload to the backend. The backend does not need to decrypt the message. It only validates the payload, checks the sender’s membership in the conversation, saves the encrypted ciphertext, creates message status rows for the recipients, and emits the message to the conversation room.&lt;br&gt;
This helped me understand that even if Socket.io is handling the real-time part, the message still has to be persisted properly. If the server only emits the message and does not save it, then the message history would be incomplete.&lt;/p&gt;

&lt;p&gt;One coding issue I had to think through was message status.&lt;br&gt;
At first, it may seem reasonable to mark a message as delivered once the server emits it to the room. But when I thought about it more, I realized that is not completely accurate. The server emitting a message does not guarantee that the recipient’s device actually received it.&lt;br&gt;
So I followed a more accurate lifecycle:&lt;br&gt;
sent → delivered → read&lt;br&gt;
When the message is saved by the server, the recipient status starts as sent. It should only become delivered when the recipient client confirms that it received the message. Then it becomes read when the recipient actually reads it.&lt;/p&gt;

&lt;p&gt;I also worked on the message:status event for delivered and read acknowledgements. One edge case I had to handle was making sure a user could not update the status of a message from another conversation. So before updating the status, the backend checks that the message actually belongs to the conversation being passed.&lt;/p&gt;

&lt;p&gt;Another part I worked on was offline message queueing with BullMQ.&lt;br&gt;
The idea is that if a recipient is offline when a message is sent, we should be able to queue a notification job for that user. The team already had a notification queue structure in the project, so instead of creating a new queue from scratch, I reused the existing queueNotification helper.&lt;br&gt;
I had to pull the latest code, inspect what already existed, and adjust my implementation instead of duplicating work.&lt;br&gt;
The flow became:&lt;br&gt;
message is sent&lt;br&gt;
message is saved&lt;br&gt;
recipient statuses are created as sent&lt;br&gt;
message is emitted to the conversation room&lt;br&gt;
offline recipients are detected&lt;br&gt;
notification jobs are queued for offline recipients&lt;br&gt;
For offline recipients, the system queues one notification job per recipient. I think this makes sense because each recipient may have their own devices, notification settings, and retry behavior.&lt;/p&gt;

&lt;p&gt;One thing that stood out to me this week was running yarn typecheck after pulling the latest main branch and seeing a lot of errors.&lt;br&gt;
My first reaction was honestly that I may have broken something. But after looking more carefully, I realized the errors were coming from other parts of the project, like Cloudinary, Multer upload routes etc.&lt;br&gt;
To confirm, I ran:&lt;br&gt;
git diff --name-only&lt;br&gt;
and saw that my current change was only in:&lt;br&gt;
src/socket/handlers/messageHandlers.ts&lt;br&gt;
That helped me separate my actual work from existing issues in the branch. I think that was an important moment for me because it reminded me not to panic when errors appear. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I Learnt&lt;/strong&gt;&lt;br&gt;
Working on Culver has helped me understand backend development in a more practical way.&lt;br&gt;
I learnt that a chat app is not just about sending messages. A proper chat system has to think about database structure, authentication, conversation membership, message persistence, real-time communication, message status, pagination, offline users, queues, and notifications.&lt;/p&gt;

&lt;p&gt;I also learnt that small decisions matter. For example, starting a message status as sent instead of delivered seems small, but it makes the system more accurate. Checking that a user belongs to a conversation before returning messages seems simple, but it is important for security. Queueing notifications per offline recipient also makes the system easier to manage later.&lt;/p&gt;

&lt;p&gt;Another big lesson was learning how to work inside a team codebase. Sometimes your work depends on what someone else has built. Sometimes another person’s changes affect your branch. So it is important to pull the latest code, inspect the structure, ask questions when needed, and avoid duplicating work.&lt;br&gt;
Building this part of Culver has made me more confident as a backend developer.&lt;/p&gt;

&lt;p&gt;Before this, I understood some of the tools individually, PostgreSQL, Knex, Express, Socket.io, Redis, and BullMQ. But working on this project helped me see how they come together to support one real product flow.&lt;br&gt;
Now, when I send a message in a chat app, I think differently about what is happening behind the scenes. I think about how the message is validated, saved, emitted, tracked, delivered, read, and possibly queued for an offline user.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>database</category>
      <category>learning</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Step-by-Step Guide to Building a Beginner-Friendly Shopping Cart Using Arrays and Functions in JavaScript</title>
      <dc:creator>Tomi Longe</dc:creator>
      <pubDate>Sat, 05 Oct 2024 22:54:22 +0000</pubDate>
      <link>https://dev.to/tomilonge/step-by-step-guide-to-building-a-beginner-friendly-shopping-cart-in-javascript-using-arrays-and-functions-4c68</link>
      <guid>https://dev.to/tomilonge/step-by-step-guide-to-building-a-beginner-friendly-shopping-cart-in-javascript-using-arrays-and-functions-4c68</guid>
      <description>&lt;p&gt;The best way to learn a new programming language is by creating as many projects as possible. You will have a smoother experience as a beginner if you build mini-projects that focus on what you have learned.&lt;br&gt;
The goal is to avoid "tutorial hell"—that scary place where you keep watching several tutorial videos without having any concrete project to show your skills—and also build the confidence necessary to tackle larger-scale projects.&lt;br&gt;
In this article, I will explain how you can create a shopping cart system as a beginner, using basic Javascript concepts.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To attempt this project, you would need to have in-depth knowledge of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Functions&lt;/li&gt;
&lt;li&gt;Methods&lt;/li&gt;
&lt;li&gt;Arrays&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What to Build?
&lt;/h2&gt;

&lt;p&gt;The shopping cart will have a system where users can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add items to cart&lt;/li&gt;
&lt;li&gt;Remove items from the cart&lt;/li&gt;
&lt;li&gt;View the cart contents&lt;/li&gt;
&lt;li&gt;Calculate the total price of items in the cart&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Step 1: Setting Up the Data
&lt;/h2&gt;

&lt;p&gt;To begin, we need to create a few arrays that will hold the data for our items. The arrays needed specifically are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;itemNames&lt;/code&gt;: Specifies the name of each item.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;itemPrices&lt;/code&gt;: Contains the price each item costs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;itemQuantities&lt;/code&gt;: Tells how much of a specific item is available.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;itemInStock&lt;/code&gt;: Determines if an item is in stock through the use of &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Laptop&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Phone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemPrices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemQuantities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemInStock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 2: Building the Shopping Cart with Functions
&lt;/h2&gt;

&lt;p&gt;We are going to create a main shopping cart function that will contain the logic for the cart. We will use closures to ensure the cart stays private and only certain functions can interact with it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ShoppingCart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addItemToCart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemInStock&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;itemNames&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt; added to the cart`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;itemNames&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt; is out of stock`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;removeItemFromCart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getCartItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;itemNames&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;calculateTotal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;itemPrices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;itemQuantities&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;addItemToCart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;removeItemFromCart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;getCartItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;calculateTotal&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To break down the code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;addItemToCart(itemIndex)&lt;/code&gt;: Adds an item to the cart based on its index (only if it is in stock).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;removeItemFromCart(itemIndex)&lt;/code&gt;: Removes an item from the cart using its index.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;getCartItems()&lt;/code&gt;: Returns the name of the items in the cart using map() to convert the indices to names.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;calculateTotal()&lt;/code&gt;: Calculates the total price by multiplying the prices and quantities of items in the cart using the &lt;code&gt;reduce()&lt;/code&gt; method.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 3: Testing the Shopping Cart
&lt;/h2&gt;

&lt;p&gt;A finished project should be tested to ensure it works as needed. We are going to test for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;adding items&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;viewing the cart&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;checking total price&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myCart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ShoppingCart&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;myCart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addItemToCart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;myCart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addItemToCart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myCart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCartItems&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; 

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myCart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculateTotal&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To break down the code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We create an instance of the shopping cart by calling it:
&lt;code&gt;const myCart = shoppingCart();&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We add items to the cart using their index from the &lt;code&gt;itemNames&lt;/code&gt; array:
&lt;code&gt;myCart.addItemToCart(0);&lt;/code&gt; for the Laptop and &lt;code&gt;myCart.addItemTocart(1);&lt;/code&gt; for the Phone.&lt;/li&gt;
&lt;li&gt;We use &lt;code&gt;getCartItems()&lt;/code&gt; to print the names of the items in the cart&lt;/li&gt;
&lt;li&gt;Finally, we calculate the total price using &lt;code&gt;calculateTotal()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Removing Items from the Cart
&lt;/h2&gt;

&lt;p&gt;A good shopping cart system must allow the user to remove items from the cart. We can do this by calling &lt;code&gt;removeItemFromCart()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;myCart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeItemFromCart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myCart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCartItems&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myCart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculateTotal&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; 

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus: Understanding Closures in the Shopping Cart System
&lt;/h2&gt;

&lt;p&gt;Closures help the cart array remain private, only accessible through the functions returned by the &lt;code&gt;ShoppingCart()&lt;/code&gt; function.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The cart array is defined inside the &lt;code&gt;shopping cart()&lt;/code&gt; and cannot be accessed directly from outside. However, because the &lt;code&gt;addItemTocart()&lt;/code&gt;, &lt;code&gt;removeItemFromCart()&lt;/code&gt;, &lt;code&gt;getCartItems()&lt;/code&gt; and &lt;code&gt;calculateTotal()&lt;/code&gt; functions are defined inside the same scope, they can interact with cart.&lt;/li&gt;
&lt;li&gt;Closures are a powerful feature of JavaScript that help maintain data privacy and structure in your code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By using basic arrays and functions, you've built a fully functional shopping cart system that can add, remove, and calculate totals for items. The awesome part of this project is that it uses closures to encapsulate and manage state without requiring complex objects or classes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Laptop&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Phone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemPrices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemQuantities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemInStock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ShoppingCart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addItemToCart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemInStock&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;itemNames&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt; added to the cart`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;itemNames&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt; is out of stock`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;removeItemFromCart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getCartItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;itemNames&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;calculateTotal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;itemPrices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;itemQuantities&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;addItemToCart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;removeItemFromCart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;getCartItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;calculateTotal&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myCart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ShoppingCart&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;myCart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addItemToCart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;myCart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addItemToCart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myCart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCartItems&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myCart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculateTotal&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;myCart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeItemFromCart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myCart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCartItems&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myCart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculateTotal&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope you enjoyed learning, and I am excited for you to build more awesome projects!&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>javascript</category>
      <category>shoppingcart</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
