iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🛒

I Built a Web App to Never Forget My Wife's Shopping List

に公開

This web app is an app I created for myself.
It's what you call a project for my own benefit.

Introduction of the Finished Product

You paste the text you copied from a LINE message sent by your wife
into the text field.

After that, it's just an app where the checklist is displayed when you press a button.
Once you've tossed the items into your shopping cart, you just check them off.

Please refer to the following for the actual app:

https://shopping-checklist-ruby.vercel.app/

Creating the Project

After moving to the directory where you want to create the project:

npx create-next-app@latest shopping-checklist

The settings are something like this.
I selected almost all the defaults.

 Would you like to use TypeScript? No / Yes #Select Yes
 Would you like to use ESLint? No / Yes #Select Yes
 Would you like to use Tailwind CSS? No / Yes #Select Yes
 Would you like your code inside a `src/` directory? No / Yes #Select Yes
 Would you like to use App Router? (recommended) … No / Yes #Select Yes
 Would you like to use Turbopack for next dev? No / Yes #Select No
 Would you like to customize the import alias (@/* by default)? … No / Yes #Select No

There are various folders after creation, but
you just need to paste the code into project-name/src/app/page.tsx.

Code

// src/app/page.tsx

"use client";

import React, { useState } from "react";

// Main component for the checklist
const ClipboardCheckList: React.FC = () => {
  // `items` is the state that holds the checklist items
  const [items, setItems] = useState<string[]>([]);
  // `manualInput` is the state that holds the string entered in the textarea
  const [manualInput, setManualInput] = useState<string>("");

  // `handlePaste` is a function that splits the content of the textarea and adds it to the list
  const handlePaste = () => {
    // Get each line from the textarea, remove empty lines, and convert to a list
    const lines = manualInput.split("\n").filter((line) => line.trim() !== "");
    // Set the list split by line into the `items` state
    setItems(lines);
    // Clear the textarea
    setManualInput("");
  };

  // `handleClear` is a function that clears the checklist
  const handleClear = () => {
    // Reset both `items` and `checkedItems` states
    setItems([]);
    setCheckedItems({});
  };

  // `checkedItems` is the state that holds the checked status of each checkbox
  const [checkedItems, setCheckedItems] = useState<{ [key: number]: boolean }>(
    {}
  );

  // `handleCheckboxChange` is a function that toggles the check status of a specific item
  const handleCheckboxChange = (index: number) => {
    // Invert the specified index in the `checkedItems` state
    setCheckedItems((prev) => ({
      ...prev,
      [index]: !prev[index],
    }));
  };

  return (
    <div className="p-4 max-w-md mx-auto">
      <h1 className="text-2xl font-bold mb-4">Shopping Checklist</h1>
      {/* Textarea for manually pasting the content */}
      <textarea
        value={manualInput}
        onChange={(e) => setManualInput(e.target.value)}
        placeholder="Paste your list here"
        className="w-full p-2 border rounded-md mb-2 text-black"
        rows={4}
      />
      {/* Action buttons for paste and clear */}
      <div className="flex space-x-2 mb-4">
        <button
          onClick={handlePaste}
          className="px-4 py-2 bg-blue-500 text-white rounded-md"
        >
          Add to List
        </button>
        <button
          onClick={handleClear}
          className="px-4 py-2 bg-red-500 text-white rounded-md"
        >
          Clear List
        </button>
      </div>
      {/* List of checklist items */}
      <ul className="space-y-2">
        {items.map((item, index) => (
          <li key={index} className="flex items-center">
            {/* Wrap checkbox and text in a label to toggle check on click */}
            <label className="flex items-center cursor-pointer">
              <input
                type="checkbox"
                checked={!!checkedItems[index]}
                onChange={() => handleCheckboxChange(index)}
                className="mr-2"
              />
              {/* Checked items are struck through and grayed out */}
              <span
                className={`${
                  checkedItems[index] ? "line-through text-gray-500" : ""
                }`}
              >
                {item}
              </span>
            </label>
          </li>
        ))}
      </ul>
    </div>
  );
};

// Function for exporting the entire page
export default function Home() {
  return <ClipboardCheckList />;
}

Closing

This time, I created a completely personal web app.
I created this because I am studying Next.js and wanted to try deploying something.

That being said, Next.js and Tailwind are so convenient...
I will continue to study and deepen my understanding.

Discussion