90% Discount Hostinger Coupon 1CODERX53
896+ Capstone Project is Available: Whatsapp: +91-7011258995, Email: sharecodepoint@gmail.com

Create a tasks tracking app with a kanban board and a confetti animation

Create a tasks tracking app with a kanban board and a confetti animation whenever tasks are completed

Creating a single HTML file that will contain the entire application. It will feature a three-column Kanban board ("To Do", "In Progress", and "Done"). You'll be able to add tasks, drag them between columns, and every time you move a task to the "Done" column, a fun confetti animation will celebrate your accomplishment. To make sure your tasks are saved, I'll integrate it with a Firestore database. The design will be clean and modern, thanks to Tailwind CSS.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Kanban Task Tracker</title>
    <!-- Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Google Fonts: Inter -->
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
    <!-- Font Awesome for icons -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        /* Custom styles for the app */
        body {
            font-family: 'Inter', sans-serif;
        }
        .kanban-board {
            display: flex;
            justify-content: space-around;
            gap: 1rem;
            padding: 1rem;
            overflow-x: auto; /* Allow horizontal scrolling on small screens */
        }
        .kanban-column {
            background-color: #f4f5f7;
            border-radius: 0.5rem;
            padding: 1rem;
            width: 300px;
            flex-shrink: 0; /* Prevent columns from shrinking */
            box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
        }
        .kanban-column-title {
            font-weight: 600;
            margin-bottom: 1rem;
            text-align: center;
            color: #172b4d;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 0.5rem;
        }
        .task-card {
            background-color: white;
            border-radius: 0.5rem;
            padding: 1rem;
            margin-bottom: 0.75rem;
            box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
            cursor: grab;
            transition: background-color 0.2s;
        }
        .task-card:hover {
            background-color: #f9fafb;
        }
        .task-card.dragging {
            opacity: 0.5;
            transform: rotate(3deg);
        }
        .drop-zone {
            min-height: 200px;
            transition: background-color 0.3s;
        }
        .drop-zone.drag-over {
            background-color: #e9eaf0;
        }
    </style>
</head>
<body class="bg-gray-100">

    <div class="container mx-auto p-4 max-w-6xl">
        <header class="text-center my-8">
            <h1 class="text-4xl font-bold text-gray-800">My Kanban Board</h1>
            <p class="text-gray-500 mt-2">Organize your tasks, get things done!</p>
            <div id="userInfo" class="text-sm text-gray-400 mt-4"></div>
        </header>

        <!-- Task Input Section -->
        <div class="max-w-md mx-auto mb-8 bg-white p-6 rounded-lg shadow-md">
             <div class="flex gap-4">
                <input type="text" id="taskInput" class="flex-grow border-2 border-gray-300 p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition" placeholder="Enter a new task...">
                <button id="addTaskBtn" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-lg transition shadow-md hover:shadow-lg">
                    <i class="fas fa-plus mr-2"></i>Add
                </button>
            </div>
        </div>

        <!-- Kanban Board -->
        <div id="kanbanBoard" class="kanban-board">
            <!-- To Do Column -->
            <div id="todo" class="kanban-column" data-status="todo">
                <h2 class="kanban-column-title"><i class="fas fa-list-alt text-orange-500"></i>To Do</h2>
                <div class="drop-zone" data-status="todo"></div>
            </div>
            <!-- In Progress Column -->
            <div id="inprogress" class="kanban-column" data-status="inprogress">
                <h2 class="kanban-column-title"><i class="fas fa-cogs text-blue-500"></i>In Progress</h2>
                <div class="drop-zone" data-status="inprogress"></div>
            </div>
            <!-- Done Column -->
            <div id="done" class="kanban-column" data-status="done">
                <h2 class="kanban-column-title"><i class="fas fa-check-circle text-green-500"></i>Done</h2>
                <div class="drop-zone" data-status="done"></div>
            </div>
        </div>
    </div>
   
    <!-- Custom Modal for alerts -->
    <div id="custom-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden">
        <div class="bg-white p-8 rounded-lg shadow-2xl text-center max-w-sm">
            <p id="modal-message" class="mb-6 text-lg text-gray-700"></p>
            <button id="modal-close-btn" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-6 rounded-lg">OK</button>
        </div>
    </div>


    <!-- Confetti JS -->
    <script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.5.1/dist/confetti.browser.min.js"></script>
   
    <!-- Firebase -->
    <script type="module">
        import { initializeApp } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-app.js";
        import { getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged, setPersistence, browserLocalPersistence } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-auth.js";
        import { getFirestore, doc, collection, addDoc, onSnapshot, updateDoc, setLogLevel } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-firestore.js";

        // --- CONFIGURATION ---
        const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : { apiKey: "YOUR_API_KEY", authDomain: "YOUR_AUTH_DOMAIN", projectId: "YOUR_PROJECT_ID" };
        const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-kanban-app';
       
        // --- INITIALIZATION ---
        const app = initializeApp(firebaseConfig);
        const db = getFirestore(app);
        const auth = getAuth(app);
       
        // --- STATE ---
        let userId = null;
        let isAuthReady = false;

        // --- UI ELEMENTS ---
        const taskInput = document.getElementById('taskInput');
        const addTaskBtn = document.getElementById('addTaskBtn');
        const kanbanBoard = document.getElementById('kanbanBoard');
        const userInfoDiv = document.getElementById('userInfo');
        const modal = document.getElementById('custom-modal');
        const modalMessage = document.getElementById('modal-message');
        const modalCloseBtn = document.getElementById('modal-close-btn');

        // --- AUTHENTICATION ---
        onAuthStateChanged(auth, async (user) => {
            if (user) {
                userId = user.uid;
                userInfoDiv.innerHTML = `User ID: <span class="font-semibold text-gray-600">${userId}</span>`;
                isAuthReady = true;
                await listenForTasks();
            } else {
                console.log("No user signed in.");
                isAuthReady = false;
            }
        });

        async function initializeAuth() {
            try {
                 await setPersistence(auth, browserLocalPersistence);
                 if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) {
                    await signInWithCustomToken(auth, __initial_auth_token);
                } else {
                    await signInAnonymously(auth);
                }
            } catch (error) {
                console.error("Authentication Error: ", error);
                showAlert(`Authentication failed: ${error.message}`);
            }
        }
       
        // --- FIRESTORE ---
        async function listenForTasks() {
            if (!isAuthReady || !userId) {
                 console.log("Auth not ready, skipping task listener.");
                 return;
            }
            const tasksCollectionRef = collection(db, `artifacts/${appId}/users/${userId}/tasks`);
            onSnapshot(tasksCollectionRef, (snapshot) => {
                clearBoard();
                snapshot.forEach((doc) => {
                    const task = { id: doc.id, ...doc.data() };
                    renderTask(task);
                });
            }, (error) => {
                console.error("Error listening for tasks:", error);
                showAlert("Could not load tasks. Please check your connection.");
            });
        }

        async function saveTask(taskText) {
             if (!isAuthReady || !userId) {
                showAlert("You must be signed in to add tasks.");
                return;
            }
            try {
                await addDoc(collection(db, `artifacts/${appId}/users/${userId}/tasks`), {
                    text: taskText,
                    status: 'todo' // Default status
                });
            } catch (error) {
                console.error("Error adding document: ", error);
                showAlert("Failed to save the task.");
            }
        }

        async function updateTaskStatus(taskId, newStatus) {
            if (!isAuthReady || !userId) return;
             try {
                const taskRef = doc(db, `artifacts/${appId}/users/${userId}/tasks`, taskId);
                await updateDoc(taskRef, { status: newStatus });
                if (newStatus === 'done') {
                    triggerConfetti();
                }
            } catch (error) {
                console.error("Error updating document: ", error);
                showAlert("Failed to update the task status.");
            }
        }


        // --- UI & DOM MANIPULATION ---
        function renderTask(task) {
            const column = document.querySelector(`.drop-zone[data-status="${task.status}"]`);
            if (!column) return;

            const taskCard = document.createElement('div');
            taskCard.id = task.id;
            taskCard.className = 'task-card';
            taskCard.draggable = true;
            taskCard.textContent = task.text;

            taskCard.addEventListener('dragstart', handleDragStart);
            taskCard.addEventListener('dragend', handleDragEnd);

            column.appendChild(taskCard);
        }

        function clearBoard() {
            document.querySelectorAll('.drop-zone').forEach(zone => {
                zone.innerHTML = '';
            });
        }
       
        function showAlert(message) {
            modalMessage.textContent = message;
            modal.classList.remove('hidden');
        }

        // --- EVENT HANDLERS ---
        addTaskBtn.addEventListener('click', () => {
            const taskText = taskInput.value.trim();
            if (taskText) {
                saveTask(taskText);
                taskInput.value = '';
            } else {
                showAlert("Please enter a task!");
            }
        });
       
        taskInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter') {
                addTaskBtn.click();
            }
        });

        modalCloseBtn.addEventListener('click', () => {
             modal.classList.add('hidden');
        });

        // Drag and Drop Handlers
        let draggedItemId = null;

        function handleDragStart(e) {
            draggedItemId = e.target.id;
            e.target.classList.add('dragging');
            e.dataTransfer.effectAllowed = 'move';
        }

        function handleDragEnd(e) {
            e.target.classList.remove('dragging');
            draggedItemId = null;
        }

        kanbanBoard.addEventListener('dragover', (e) => {
            e.preventDefault();
            const dropZone = e.target.closest('.drop-zone');
            if (dropZone) {
                dropZone.classList.add('drag-over');
            }
        });

        kanbanBoard.addEventListener('dragleave', (e) => {
            const dropZone = e.target.closest('.drop-zone');
            if (dropZone) {
                dropZone.classList.remove('drag-over');
            }
        });
       
        kanbanBoard.addEventListener('drop', async (e) => {
            e.preventDefault();
            const dropZone = e.target.closest('.drop-zone');
            if (dropZone && draggedItemId) {
                dropZone.classList.remove('drag-over');
                const draggedItem = document.getElementById(draggedItemId);
                const newStatus = dropZone.dataset.status;
               
                // Only update if status is different
                if (draggedItem.parentElement !== dropZone) {
                    await updateTaskStatus(draggedItemId, newStatus);
                    // The onSnapshot listener will handle re-rendering,
                    // but we can move it optimistically for better UX
                    dropZone.appendChild(draggedItem);
                }
            }
        });

        // --- EFFECTS ---
        function triggerConfetti() {
            confetti({
                particleCount: 150,
                spread: 90,
                origin: { y: 0.6 }
            });
        }

        // --- APP START ---
        window.onload = function() {
            initializeAuth();
        };

    </script>
</body>
</html>


break down the code for the Kanban Task Tracker app and explain each part.

The entire application is built within a single HTML file, which can be divided into three main sections:

  1. HTML (The Structure): This defines all the visible elements on the page.

    • Header: Contains the main title "My Kanban Board" and a placeholder (<div id="userInfo">) to show the User ID once you're connected.
    • Task Input: A simple form with a text box (<input id="taskInput">) for you to type new tasks and an "Add" button (<button id="addTaskBtn">).
    • Kanban Board: The core of the app. It's a main container (<div id="kanbanBoard">) that holds three columns: "To Do," "In Progress," and "Done."
    • Columns & Drop Zones: Each column (<div class="kanban-column">) has a title and a dedicated area (<div class="drop-zone">) where the task cards will appear. These zones are what detect when you drag a task over them.
    • Modal: A hidden popup (<div id="custom-modal">) that is used to show alerts, like when you try to add an empty task.
  2. CSS (The Style): This makes the app look good.

    • Tailwind CSS: Most of the styling is done using Tailwind's utility classes directly in the HTML (e.g., bg-blue-600, rounded-lg, shadow-md). This is a modern way to style without writing a lot of custom CSS.
    • Custom Styles: The <style> block in the <head> section adds some specific styles that are easier to manage separately. This includes the overall layout of the Kanban board (.kanban-board), the look of the columns (.kanban-column), and the appearance of the task cards (.task-card). It also defines styles for the dragging animation (.task-card.dragging) and highlighting the column you're dragging over (.drop-zone.drag-over).
  3. JavaScript (The Brains): This handles all the logic and interactivity. It's enclosed in <script type="module"> at the bottom.

    • Initialization: It sets up the connection to the Firebase database using the configuration details provided.
    • Authentication: The initializeAuth function signs you in anonymously or with a provided token. The onAuthStateChanged listener waits for the sign-in to complete, gets your unique userId, and then calls the function to load your tasks.
    • Firestore Database Logic:
      • saveTask: Takes the text from the input field and saves it as a new document in your private collection in the Firestore database with a default status of "todo".
      • listenForTasks: This is a real-time listener. It automatically receives updates from the database. Whenever a task is added, or its status changes, it redraws the tasks on the board.
      • updateTaskStatus: When you drop a task into a new column, this function is called to update the status field for that task in the database.
    • Drag and Drop: A set of event handlers (handleDragStart, dragover, drop, etc.) manage the process of picking up a task card, moving it across the screen, and dropping it into a new column.
    • Event Handlers:
      • The "Add" button's click listener calls saveTask.
      • Pressing "Enter" in the input field also triggers the "Add" button.
    • Confetti! When updateTaskStatus changes a task's status to "done", it calls triggerConfetti() to create the celebratory animation.

In short, the HTML creates the skeleton, the CSS makes it look good, and the JavaScript connects to the database and brings it all to life by handling user input and data changes.

Sharecodepoint

Sharecodepoint is the junction where every essential thing is shared for college students in the well-defined packets of codes. We are focused on providing you the best material package like Question papers, MCQ'S, and one NIGHT STUDY MATERIAL. facebook twitter youtube instagram

Post a Comment

Previous Post Next Post

Contact Form