<li class="tBRLg6uDC7sSyw" data-testid="list-wrapper" data-list-id="6363e1c491538c03627484ca" data-drop-target-for-element="true"><div class="Sb_QqNKeadm2oq" data-testid="list" data-drag-scroll-disabled="true" data-drop-target-for-external="true"><div class="bPNGI_VbtbXQ8v" data-testid="list-header" draggable="true"><div class="mKJWg6W_CLHoiO"><h2 dir="auto" class="KLvU2mDGTQrsWG" role="textbox" data-testid="list-name">To Do Today</h2><textarea data-testid="list-name-textarea" aria-label="To Do Today" class="XmUgdJ5MepKUCB" dir="auto" maxlength="512" spellcheck="false" style="height: 32px;">To Do Today</textarea></div><button class="H2KpqQShz7Nqjb bxgKMAm3lq5BpA SEj5vUdI3VvxDc" type="button"><span role="img" aria-label="Collapse list" class="css-snhnyn" style="--icon-primary-color: currentColor; --icon-secondary-color: var(--ds-surface, #FFFFFF);"><svg width="24" height="24" viewBox="0 0 24 24" role="presentation"><path fill="currentColor" d="M8.062 11 6.5 9.914A1 1 0 0 1 7.914 8.5l2.616 2.616c.28.167.47.5.47.884s-.19.717-.47.884L7.914 15.5A1 1 0 1 1 6.5 14.086L8.062 13h-3.68c-.487 0-.882-.448-.882-1s.395-1 .882-1zm5.408 1.884c-.28-.167-.47-.5-.47-.884s.19-.717.47-.884L16.086 8.5A1 1 0 0 1 17.5 9.914L15.938 11h3.68c.487 0 .882.448.882 1s-.395 1-.882 1h-3.68l1.562 1.086a1 1 0 0 1-1.414 1.414z"></path></svg></span></button><div role="presentation"><button class="x7x105F0Ex0A7R bxgKMAm3lq5BpA iUcMblFAuq9LKn HAVwIqCeMHpVKh SEj5vUdI3VvxDc" type="button" data-testid="list-edit-menu-button" aria-haspopup="true"><span class="nch-icon A3PtEe1rGIm_yL neoUEAwI0GETBQ fAvkXZrzkeHLoc"><span data-testid="OverflowMenuHorizontalIcon" data-vc="icon-OverflowMenuHorizontalIcon" aria-hidden="true" class="css-snhnyn" style="--icon-primary-color: currentColor; --icon-secondary-color: inherit;"><svg width="24" height="24" role="presentation" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M5 14C6.10457 14 7 13.1046 7 12C7 10.8954 6.10457 10 5 10C3.89543 10 3 10.8954 3 12C3 13.1046 3.89543 14 5 14ZM12 14C13.1046 14 14 13.1046 14 12C14 10.8954 13.1046 10 12 10C10.8954 10 10 10.8954 10 12C10 13.1046 10.8954 14 12 14ZM21 12C21 13.1046 20.1046 14 19 14C17.8954 14 17 13.1046 17 12C17 10.8954 17.8954 10 19 10C20.1046 10 21 10.8954 21 12Z" fill="currentColor"></path></svg></span></span></button></div></div><div class="Ddjfff57W7abTr" data-testid="list-cards-top-buffer"></div><ol class="RD2CmKQFZKidd6" data-testid="list-cards" data-auto-scrollable="true"><li class="T9JQSaXUsHTEzk" data-testid="list-card" draggable="true" data-drop-target-for-element="true"><div><div class="KWQlnMvysRK4fI" data-testid="trello-card" data-card-id="66ed2c1a050b54659a4320e7" data-drop-target-for-external="true" data-drop-target-for-element="true"><div class="amUfYqLTZOvGsn"><a draggable="false" class="NdQKKfeqJDDdX3" dir="auto" href="/c/xJqWe3Bj/666-research-and-design-pipeline-for-ml-interview" data-testid="card-name">Research and design pipeline for ML Interview</a><div class="jS9THwzyUow79A" data-testid="card-front-badges"></div></div><div role="presentation"><button class="o0opcYgoysGMA4 bxgKMAm3lq5BpA HAVwIqCeMHpVKh SEj5vUdI3VvxDc" type="button" data-testid="quick-card-editor-button" aria-label="Edit card" tabindex="-1"><span class="nch-icon A3PtEe1rGIm_yL neoUEAwI0GETBQ fAvkXZrzkeHLoc"><span data-testid="EditIcon" data-vc="icon-EditIcon" aria-hidden="true" class="css-snhnyn" style="--icon-primary-color: var(--ds-text-accent-gray-bolder, #172B4D); --icon-secondary-color: inherit;"><svg width="24" height="24" role="presentation" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.82034 14.4893L9.94134 16.6103L18.4303 8.12131L16.3093 6.00031H16.3073L7.82034 14.4893ZM17.7233 4.58531L19.8443 6.70731C20.6253 7.48831 20.6253 8.7543 19.8443 9.53531L10.0873 19.2933L5.13734 14.3433L14.8943 4.58531C15.2853 4.19531 15.7973 4.00031 16.3093 4.00031C16.8203 4.00031 17.3323 4.19531 17.7233 4.58531ZM5.20094 20.4097C4.49794 20.5537 3.87694 19.9327 4.02094 19.2297L4.80094 15.4207L9.00994 19.6297L5.20094 20.4097Z" fill="currentColor"></path></svg></span></span></button></div></div></div></li><li class="T9JQSaXUsHTEzk" data-testid="list-card" draggable="true" data-drop-target-for-element="true"><div><div class="KWQlnMvysRK4fI" data-testid="trello-card" data-card-id="66ebd18152661b8509426321" data-drop-target-for-external="true" data-drop-target-for-element="true"><div class="amUfYqLTZOvGsn"><a draggable="false" class="NdQKKfeqJDDdX3" dir="auto" href="/c/eoMWT9JS/657-call-kaustubh-about-job" data-testid="card-name">Call Kaustubh about Job</a><div class="jS9THwzyUow79A" data-testid="card-front-badges"></div></div><div role="presentation"><button class="o0opcYgoysGMA4 bxgKMAm3lq5BpA HAVwIqCeMHpVKh SEj5vUdI3VvxDc" type="button" data-testid="quick-card-editor-button" aria-label="Edit card" tabindex="-1"><span class="nch-icon A3PtEe1rGIm_yL neoUEAwI0GETBQ fAvkXZrzkeHLoc"><span data-testid="EditIcon" data-vc="icon-EditIcon" aria-hidden="true" class="css-snhnyn" style="--icon-primary-color: var(--ds-text-accent-gray-bolder, #172B4D); --icon-secondary-color: inherit;"><svg width="24" height="24" role="presentation" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.82034 14.4893L9.94134 16.6103L18.4303 8.12131L16.3093 6.00031H16.3073L7.82034 14.4893ZM17.7233 4.58531L19.8443 6.70731C20.6253 7.48831 20.6253 8.7543 19.8443 9.53531L10.0873 19.2933L5.13734 14.3433L14.8943 4.58531C15.2853 4.19531 15.7973 4.00031 16.3093 4.00031C16.8203 4.00031 17.3323 4.19531 17.7233 4.58531ZM5.20094 20.4097C4.49794 20.5537 3.87694 19.9327 4.02094 19.2297L4.80094 15.4207L9.00994 19.6297L5.20094 20.4097Z" fill="currentColor"></path></svg></span></span></button></div></div></div></li><li class="T9JQSaXUsHTEzk" data-testid="list-card" draggable="true" data-drop-target-for-element="true"><div><div class="KWQlnMvysRK4fI" data-testid="trello-card" data-card-id="66d7f49c0a9e6a432bd50451" data-drop-target-for-external="true" data-drop-target-for-element="true"><div class="amUfYqLTZOvGsn"><div class="Ln_WDlu2A5sGGO"><div class="fHs2_y2vnMhfvh"><span class="CNnougcEO0WaO3 yuJNNxl_DN8j38 YjsahfcWMjpk4W IDeXAnQiLBnlva color-blind-pattern-red MNuX_OT6g8BvrC" tabindex="-1" aria-label="Color: red, title: none" data-color="red" data-expanded="false" data-testid="compact-card-label"></span></div></div><a draggable="false" class="NdQKKfeqJDDdX3" dir="auto" href="/c/YxUEYr7X/624-finish-debugging-ant" data-testid="card-name">Finish debugging Ant</a><div class="jS9THwzyUow79A" data-testid="card-front-badges"></div></div><div role="presentation"><button class="o0opcYgoysGMA4 bxgKMAm3lq5BpA HAVwIqCeMHpVKh SEj5vUdI3VvxDc" type="button" data-testid="quick-card-editor-button" aria-label="Edit card" tabindex="-1"><span class="nch-icon A3PtEe1rGIm_yL neoUEAwI0GETBQ fAvkXZrzkeHLoc"><span data-testid="EditIcon" data-vc="icon-EditIcon" aria-hidden="true" class="css-snhnyn" style="--icon-primary-color: var(--ds-text-accent-gray-bolder, #172B4D); --icon-secondary-color: inherit;"><svg width="24" height="24" role="presentation" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.82034 14.4893L9.94134 16.6103L18.4303 8.12131L16.3093 6.00031H16.3073L7.82034 14.4893ZM17.7233 4.58531L19.8443 6.70731C20.6253 7.48831 20.6253 8.7543 19.8443 9.53531L10.0873 19.2933L5.13734 14.3433L14.8943 4.58531C15.2853 4.19531 15.7973 4.00031 16.3093 4.00031C16.8203 4.00031 17.3323 4.19531 17.7233 4.58531ZM5.20094 20.4097C4.49794 20.5537 3.87694 19.9327 4.02094 19.2297L4.80094 15.4207L9.00994 19.6297L5.20094 20.4097Z" fill="currentColor"></path></svg></span></span></button></div></div></div></li><li class="T9JQSaXUsHTEzk" data-testid="list-card" draggable="true" data-drop-target-for-element="true"><div><div class="KWQlnMvysRK4fI" data-testid="trello-card" data-card-id="66d6a14808fce1af6d7ae186" data-drop-target-for-external="true" data-drop-target-for-element="true"><div class="amUfYqLTZOvGsn"><div class="Ln_WDlu2A5sGGO"><div class="fHs2_y2vnMhfvh"><span class="CNnougcEO0WaO3 yuJNNxl_DN8j38 YjsahfcWMjpk4W IDeXAnQiLBnlva color-blind-pattern-red MNuX_OT6g8BvrC" tabindex="-1" aria-label="Color: red, title: none" data-color="red" data-expanded="false" data-testid="compact-card-label"></span></div></div><a draggable="false" class="NdQKKfeqJDDdX3" dir="auto" href="/c/KOMnm5B8/616-plan-next-day" data-testid="card-name">Plan next day</a><div class="jS9THwzyUow79A" data-testid="card-front-badges"></div></div><div role="presentation"><button class="o0opcYgoysGMA4 bxgKMAm3lq5BpA HAVwIqCeMHpVKh SEj5vUdI3VvxDc" type="button" data-testid="quick-card-editor-button" aria-label="Edit card" tabindex="-1"><span class="nch-icon A3PtEe1rGIm_yL neoUEAwI0GETBQ fAvkXZrzkeHLoc"><span data-testid="EditIcon" data-vc="icon-EditIcon" aria-hidden="true" class="css-snhnyn" style="--icon-primary-color: var(--ds-text-accent-gray-bolder, #172B4D); --icon-secondary-color: inherit;"><svg width="24" height="24" role="presentation" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.82034 14.4893L9.94134 16.6103L18.4303 8.12131L16.3093 6.00031H16.3073L7.82034 14.4893ZM17.7233 4.58531L19.8443 6.70731C20.6253 7.48831 20.6253 8.7543 19.8443 9.53531L10.0873 19.2933L5.13734 14.3433L14.8943 4.58531C15.2853 4.19531 15.7973 4.00031 16.3093 4.00031C16.8203 4.00031 17.3323 4.19531 17.7233 4.58531ZM5.20094 20.4097C4.49794 20.5537 3.87694 19.9327 4.02094 19.2297L4.80094 15.4207L9.00994 19.6297L5.20094 20.4097Z" fill="currentColor"></path></svg></span></span></button></div></div></div></li><li class="T9JQSaXUsHTEzk" data-testid="list-card" draggable="true" data-drop-target-for-element="true"><div><div class="KWQlnMvysRK4fI" data-testid="trello-card" data-card-id="66ed2b85f9b420ab8495fa8f" data-drop-target-for-external="true" data-drop-target-for-element="true"><div class="amUfYqLTZOvGsn"><a draggable="false" class="NdQKKfeqJDDdX3" dir="auto" href="/c/rcbCOopQ/665-birthday-at-8-9-pm" data-testid="card-name">Birthday at 8/9 pm</a><div class="jS9THwzyUow79A" data-testid="card-front-badges"></div></div><div role="presentation"><button class="o0opcYgoysGMA4 bxgKMAm3lq5BpA HAVwIqCeMHpVKh SEj5vUdI3VvxDc" type="button" data-testid="quick-card-editor-button" aria-label="Edit card" tabindex="-1"><span class="nch-icon A3PtEe1rGIm_yL neoUEAwI0GETBQ fAvkXZrzkeHLoc"><span data-testid="EditIcon" data-vc="icon-EditIcon" aria-hidden="true" class="css-snhnyn" style="--icon-primary-color: var(--ds-text-accent-gray-bolder, #172B4D); --icon-secondary-color: inherit;"><svg width="24" height="24" role="presentation" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.82034 14.4893L9.94134 16.6103L18.4303 8.12131L16.3093 6.00031H16.3073L7.82034 14.4893ZM17.7233 4.58531L19.8443 6.70731C20.6253 7.48831 20.6253 8.7543 19.8443 9.53531L10.0873 19.2933L5.13734 14.3433L14.8943 4.58531C15.2853 4.19531 15.7973 4.00031 16.3093 4.00031C16.8203 4.00031 17.3323 4.19531 17.7233 4.58531ZM5.20094 20.4097C4.49794 20.5537 3.87694 19.9327 4.02094 19.2297L4.80094 15.4207L9.00994 19.6297L5.20094 20.4097Z" fill="currentColor"></path></svg></span></span></button></div></div></div></li></ol><div class="IapUGb_Cq2VzSr" data-testid="list-footer"><button class="O9vivwyDxMqo3q bxgKMAm3lq5BpA iUcMblFAuq9LKn PnEv2xIWy3eSui SEj5vUdI3VvxDc" type="button" data-testid="list-add-card-button"><span class="nch-icon A3PtEe1rGIm_yL neoUEAwI0GETBQ gNDonYrNSA1GXJ"><span data-testid="AddIcon" data-vc="icon-AddIcon" aria-hidden="true" class="css-snhnyn" style="--icon-primary-color: currentColor; --icon-secondary-color: inherit;"><svg width="24" height="24" role="presentation" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 3C11.4477 3 11 3.44772 11 4V11L4 11C3.44772 11 3 11.4477 3 12C3 12.5523 3.44772 13 4 13H11V20C11 20.5523 11.4477 21 12 21C12.5523 21 13 20.5523 13 20V13H20C20.5523 13 21 12.5523 21 12C21 11.4477 20.5523 11 20 11L13 11V4C13 3.44772 12.5523 3 12 3Z" fill="currentColor"></path></svg></span></span>Add a card</button><button class="xJM_gd_GpGM0yl bxgKMAm3lq5BpA iUcMblFAuq9LKn HAVwIqCeMHpVKh SEj5vUdI3VvxDc" type="button" data-testid="card-template-list-button" aria-label="Create from template…"><span class="nch-icon A3PtEe1rGIm_yL neoUEAwI0GETBQ fAvkXZrzkeHLoc"><span data-testid="TemplateCardIcon" data-vc="icon-TemplateCardIcon" aria-hidden="true" class="css-snhnyn" style="--icon-primary-color: currentColor; --icon-secondary-color: inherit;"><svg width="24" height="24" role="presentation" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M3 6V5C3 3.89543 3.89543 3 5 3H6C6.55228 3 7 3.44772 7 4C7 4.55228 6.55228 5 6 5H5V6C5 6.55228 4.55228 7 4 7C3.44772 7 3 6.55228 3 6Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M6 8C6 6.89543 6.89543 6 8 6H19C20.1046 6 21 6.89543 21 8V18C21 19.1046 20.1046 20 19 20H8C6.89543 20 6 19.1046 6 18V8ZM8 8H19V14H8V8ZM18 18C17.4477 18 17 17.5523 17 17C17 16.4477 17.4477 16 18 16C18.5523 16 19 16.4477 19 17C19 17.5523 18.5523 18 18 18ZM8 17C8 17.5523 8.44772 18 9 18H12C12.5523 18 13 17.5523 13 17C13 16.4477 12.5523 16 12 16H9C8.44772 16 8 16.4477 8 17Z" fill="currentColor"></path><path d="M4 14C3.44772 14 3 14.4477 3 15V16C3 17.1046 3.89543 18 5 18V15C5 14.4477 4.55228 14 4 14Z" fill="currentColor"></path><path d="M3 9C3 8.44772 3.44772 8 4 8C4.55228 8 5 8.44772 5 9V12C5 12.5523 4.55228 13 4 13C3.44772 13 3 12.5523 3 12V9Z" fill="currentColor"></path><path d="M8 4C8 3.44772 8.44772 3 9 3H13C13.5523 3 14 3.44772 14 4C14 4.55228 13.5523 5 13 5H9C8.44772 5 8 4.55228 8 4Z" fill="currentColor"></path><path d="M16 3C15.4477 3 15 3.44772 15 4C15 4.55228 15.4477 5 16 5H19C19 3.89543 18.1046 3 17 3H16Z" fill="currentColor"></path></svg></span></span></button></div></div></li>

The above is the html for one column called To Do Today from Trello
Below, I'm adding my templates/index.html and static/js/script.js:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Task Productivity App</title>
        <link rel="stylesheet" href="{{ url_for('static', filename='common.css') }}">
        <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
        <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    </head>
<body>
    <div id="menu-toggle">&#9776;</div>
    <nav id="slide-menu">
        <ul>
            <li><a href="/">Habit Tracker</a></li>
            <li><a href="/yearly-grid">Yearly Grid</a></li>
            <input type="text" id="new-habit" placeholder="New habit">
            <button onclick="addHabit()">Add Habit</button>
        </ul>
    </nav>
    
    <div id="content">
        <div id="habit-tracker" class="page">
            <div class="habit-tracker">
                <h2>Habit Tracker</h2>
                <div class="habits-container" id="habits"></div>
            </div>
        </div>
    </div>

    <div class="container">
        <div class="column" id="to-do-week">
            <div class="column-header">
                <span class="column-title">To Do This Week</span>
                <span>...</span>
            </div>
        </div>
        <div class="column" id="to-do-today">
            <div class="column-header">
                <span class="column-title">To Do Today</span>
                <span>...</span>
            </div>
        </div>
        <div class="column" id="in-progress">
            <div class="column-header">
                <span class="column-title">In Progress</span>
                <span>...</span>
            </div>
        </div>
        <div class="column" id="finished">
            <div class="column-header">
                <span class="column-title">Finished</span>
                <span>...</span>
            </div>
        </div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
    <script src="{{ url_for('static', filename='js/script.js') }}"></script>
</body>
</html>

const columnOrder = ['to-do-week', 'to-do-today', 'in-progress', 'finished'];

function loadTasks() {
    axios.get('/api/tasks')
        .then(response => {
            const tasks = response.data;

            // Clear columns
            columnOrder.forEach(status => {
                const column = document.getElementById(status);
                column.innerHTML = `
                    <div class="column-header">
                        <span class="column-title">${column.id.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ')}</span>
                        <span>...</span>
                    </div>
                `;
            });

            tasks.forEach(task => {
                const columnElement = document.getElementById(task.status);
                if (!columnElement) {
                    // Skip tasks with statuses that don't have corresponding columns
                    console.warn(`No column found for status '${task.status}'. Skipping task ID ${task.id}.`);
                    return;
                }

                const taskElement = document.createElement('div');
                taskElement.className = 'task-card';
                taskElement.draggable = true;

                taskElement.dataset.taskId = task.id;
                taskElement.innerHTML = `
                    <div class="task-title" contenteditable="true" onblur="updateTaskTitle(${task.id}, this)">${task.title}</div>
                    <div class="task-buttons">
                        ${getButtonsForTask(task)}
                    </div>
                `;

                taskElement.addEventListener('dragstart', handleDragStart);
                taskElement.addEventListener('dragend', handleDragEnd);
                taskElement.addEventListener('dragover', handleDragOver);

                columnElement.appendChild(taskElement);
            });

            // Add "Add a card" buttons to columns
            columnOrder.forEach(status => {
                const column = document.getElementById(status);
                column.addEventListener('dragover', handleDragOver);
                column.addEventListener('drop', handleDrop);

                const addCard = document.createElement('div');
                addCard.className = 'add-card';
                addCard.textContent = '+ Add a card';
                addCard.onclick = () => addTask(status);
                column.appendChild(addCard);
            });
        })
        .catch(error => {
            console.error('Error loading tasks:', error);
        });
}

let draggedTask = null;
let placeholder = null;

function handleDragStart(event) {
    draggedTask = event.target;
    event.dataTransfer.effectAllowed = 'move';
    event.dataTransfer.setData('text/html', draggedTask.outerHTML);
    draggedTask.classList.add('dragging');
    
    // Create placeholder
    placeholder = document.createElement('div');
    placeholder.className = 'task-card placeholder';
    placeholder.style.height = `${draggedTask.offsetHeight}px`;
}

function handleDragOver(event) {
    event.preventDefault();
    const column = event.currentTarget;
    const afterElement = getDragAfterElement(column, event.clientY);
    
    if (afterElement) {
        column.insertBefore(placeholder, afterElement);
    } else {
        const lastTaskCard = column.querySelector('.task-card:last-of-type');
        if (lastTaskCard) {
            column.insertBefore(placeholder, lastTaskCard.nextSibling);
        }
    }
}

function handleDrop(event) {
    event.preventDefault();
    const column = event.currentTarget;
    column.removeChild(placeholder);
    
    if (draggedTask.parentElement !== column) {
        const lastTaskCard = column.querySelector('.task-card:last-of-type');
        if (lastTaskCard) {
            column.insertBefore(draggedTask, lastTaskCard.nextSibling);
        } else {
            column.insertBefore(draggedTask, column.querySelector('.add-card'));
        }
        moveTask(draggedTask.dataset.taskId, column.id);
    } else {
        const afterElement = getDragAfterElement(column, event.clientY);
        if (afterElement) {
            column.insertBefore(draggedTask, afterElement);
        } else {
            const lastTaskCard = column.querySelector('.task-card:last-of-type');
            if (lastTaskCard && lastTaskCard !== draggedTask) {
                column.insertBefore(draggedTask, lastTaskCard.nextSibling);
            }
        }
    }
    updateTaskOrder(column);
}

function handleDragEnd(event) {
    draggedTask.classList.remove('dragging');
    draggedTask = null;
    if (placeholder && placeholder.parentNode) {
        placeholder.parentNode.removeChild(placeholder);
    }
    placeholder = null;
}

function getDragAfterElement(column, y) {
    const draggableElements = [...column.querySelectorAll('.task-card:not(.dragging):not(.placeholder)')];
    return draggableElements.reduce((closest, child) => {
        const box = child.getBoundingClientRect();
        const offset = y - box.top - box.height / 2;
        if (offset < 0 && offset > closest.offset) {
            return { offset: offset, element: child };
        } else {
            return closest;
        }
    }, { offset: Number.NEGATIVE_INFINITY }).element;
}

function updateTaskOrder(column) {
    const tasks = column.querySelectorAll('.task-card');
    const order = Array.from(tasks).map(task => task.dataset.taskId);
    axios.put('/api/tasks/order', { order, status: column.id }) //! WE don't have an api/tasks/order!
        .then(() => loadTasks());
}

function getButtonsForTask(task) {
    switch (task.status) {
        case 'to-do-week':
            return `<button class="task-button" onclick="moveTask(${task.id}, 'to-do-today')">→ Today</button>`;
        case 'to-do-today':
            return `
                <button class="task-button" onclick="moveTask(${task.id}, 'in-progress')">▶ IP</button>
                <button class="task-button" onclick="moveTask(${task.id}, 'finished')">✓</button>
            `;
        case 'in-progress':
            return `
                <button class="task-button" onclick="moveTask(${task.id}, 'to-do-week')">↺ Week</button>
                <button class="task-button" onclick="moveTask(${task.id}, 'to-do-today')">↺ Today</button>
                <button class="task-button" onclick="moveTask(${task.id}, 'finished')">✓</button>
            `;
        case 'finished':
            return `
                <button class="task-button" onclick="moveTask(${task.id}, 'to-do-week')">↺ Week</button>
                <button class="task-button" onclick="moveTask(${task.id}, 'to-do-today')">↺ Today</button>
            `;
        default:
            return '';
    }
}

function addTask(status) {
    const column = document.getElementById(status);
    
    // Hide the "Add a card" button
    const addCardButton = column.querySelector('.add-card');
    addCardButton.style.display = 'none';

    // Create a new task card div
    const newCard = document.createElement('div');
    newCard.classList.add('task-card');
    newCard.contentEditable = 'true';  // Make the new card editable

    // Add event listener to handle saving when 'Enter' is pressed
    newCard.addEventListener('keydown', function(event) {
        if (event.key === 'Enter') {
            event.preventDefault();  // Prevent new lines when pressing Enter
            const taskTitle = newCard.textContent.trim();
            if (taskTitle) {
                // Send task title and status to the backend
                axios.post('/api/tasks', { title: taskTitle, status })
                    .then(() => {
                        loadTasks();  // Reload tasks after adding new one
                        addCardButton.style.display = 'block';  // Show "Add a card" button again
                    });
            } else {
                // Remove the card if no text was entered
                newCard.remove();
                addCardButton.style.display = 'block';  // Show "Add a card" button again
            }
        }
    });

    // Add event listener for "Escape" key to discard the card
    newCard.addEventListener('keydown', function(event) {
        if (event.key === 'Escape') {
            newCard.remove();  // Discard the new card
            addCardButton.style.display = 'block';  // Show "Add a card" button again
        }
    });

    // Append the new card to the column
    column.appendChild(newCard);
    // Focus on the new card to allow typing immediately
    newCard.focus();
}

function moveTask(taskId, newStatus) {
    axios.put(`/api/tasks/${taskId}`, { status: newStatus })
        .then(() => loadTasks());
}

function updateTaskTitle(taskId, element) {
    const newTitle = element.textContent.trim();
    axios.put(`/api/tasks/${taskId}`, { title: newTitle })
    .then((response) => {
        if (response.data.deleted) {
            // If the task was deleted (empty title), remove it from the UI
            element.closest('.task-card').remove();
        } else {
            // If the task was updated, reload tasks to reflect any changes
            loadTasks();
        }
    })
    .catch((error) => {
        console.error('Error updating task:', error);
        loadTasks(); // Reload tasks to ensure UI is in sync with server
    });
}

function loadHabits() {
    axios.get('/api/habits')
        .then(response => {
            const habits = response.data;
            const habitsContainer = document.getElementById('habits');
            habitsContainer.innerHTML = '';
            const today = new Date();
            habits.forEach(habit => {
                const habitElement = document.createElement('div');
                const today = new Date().toISOString().split('T')[0];
                const isAvailableToday = habit.days.includes(today.getDay().toString());
                const isCompletedToday = habit.dates.some(date => 
                    new Date(date).toDateString() === today.toDateString()
                );
                habitElement.className = `habit ${isAvailableToday ? 'habit-available' : 'habit-unavailable'} ${isCompletedToday ? 'habit-complete' : 'habit-incomplete'}`;
                habitElement.textContent = habit.name;
                //if (isAvailableToday) {
                    habitElement.onclick = () => toggleHabit(habit.id);
                //}
                habitsContainer.appendChild(habitElement);
            });
        });
}

function addHabit() {
    const name = document.getElementById('new-habit').value;
    const days = []; // You might want to add a way for users to select days
    if (name) {
        axios.post('/api/habits', { name, days })
            .then(() => {
                document.getElementById('new-habit').value = '';
                loadHabits();
            });
    }
}

function toggleHabit(habitId) {
    const today = new Date().toISOString().split('T')[0];
    axios.put(`/api/habits/${habitId}`, { toggle_date: today })
    .then(response => {
        if (response.data.success) {
            console.log(`Habit ${habitId} toggled successfully`);
            loadHabits();
        } else {
            console.error(`Failed to toggle habit ${habitId}`);
        }
    })
    .catch(error => {
        console.error('Error toggling habit:', error.response ? error.response.data : error.message);
    });
}

document.getElementById('menu-toggle').addEventListener('click', function() {
    document.body.classList.toggle('menu-active');
});

// Load tasks and habits on page load
loadTasks();
loadHabits();

I'm thinking I could do something better with my html or javascript so that I can easily reorder tasks while still keeping all my other features, including editing after creating tasks and adding new tasks. Does my html or js need a structural overhaul so that I don't need to call my app.py (attached below again) to reorder tasks?

# app.py
from flask import Flask, request, jsonify, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from datetime import date
import os
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
from apscheduler.schedulers.background import BackgroundScheduler
import logging
import requests

app = Flask(__name__)
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'app.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)
migrate = Migrate(app, db)

def archive_tasks():
    with app.app_context():
        # Check last archive date
        last_archived_date = ArchiveInfo.get_last_archive()
        today = date.today()

        # If the last archive was not today, archive and update the record
        if last_archived_date is None or last_archived_date != today:
            # Perform the archive
            tasks = Task.query.filter(Task.status == 'finished')
            for task in tasks:
                task.status = 'archived'
            db.session.commit()

            # Update the archive info in the database
            ArchiveInfo.update_last_archive(today)
            logging.info(f"Archived tasks for {today}")
        

# Configure logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('apscheduler').setLevel(logging.DEBUG)

# Initialize the scheduler
executors = {
    'default': ThreadPoolExecutor(20),
    'processpool': ProcessPoolExecutor(5)
}

job_defaults = {
    'coalesce': True, 
    'max_instances': 1
}

scheduler = BackgroundScheduler(executors=executors, job_defaults=job_defaults)
scheduler.add_job(archive_tasks, 'cron', hour=0, minute=0)  # Runs at 12:00 AM
# Periodic job to check every 15 minutes if archiving was missed
scheduler.add_job(archive_tasks, 'interval', minutes=15)
scheduler.start()

# Models
class ArchiveInfo(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    last_archive = db.Column(db.Date, nullable=False)

    @staticmethod
    def get_last_archive():
        archive_info = ArchiveInfo.query.first()
        if archive_info:
            return archive_info.last_archive
        return None

    @staticmethod
    def update_last_archive(new_archive_date):
        archive_info = ArchiveInfo.query.first()
        if not archive_info:
            archive_info = ArchiveInfo(last_archive=new_archive_date)
            db.session.add(archive_info)
        else:
            archive_info.last_archive = new_archive_date
        db.session.commit()

class Task(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    status = db.Column(db.String(20), nullable=False)
    completion_date = db.Column(db.Date)
    column = db.Column(db.String(20), default='To Do')
    def to_dict(self):
        return {
            'id': self.id,
            'title': self.title,
            'status': self.status,
            'completion_date': self.completion_date.isoformat() if self.completion_date else None,
            'column': self.column
        }

class Habit(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    #days that the habit is available
    days = db.Column(db.String(100))  # Store as comma-separated string
    #list of completed dates
    dates = db.relationship('HabitDate', backref='habit', lazy=True)

class HabitDate(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    habit_id = db.Column(db.Integer, db.ForeignKey('habit.id'), nullable=False)
    date = db.Column(db.Date, nullable=False)

class JournalEntry(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.Text, nullable=False)
    date = db.Column(db.Date, nullable=False)
    ai_comment = db.Column(db.Text)

# Routes
@app.route('/')
def index():
    return render_template('index.html')

@app.route('/yearly-grid')
def yearly_grid():
    return render_template('yearly_grid.html')

# API routes
@app.route('/api/tasks', methods=['GET', 'POST'])
def handle_tasks():
    '''
    POST request: Creates a new task in the database and returns its details.
    GET  request: Retrieves and returns all tasks from the database as a list in JSON format.
    '''
    if request.method == 'POST':
        data = request.json
        new_task = Task(
            title=data['title'],
            status=data['status'],
            column=data.get('column', 'To Do')
        )
        db.session.add(new_task)
        db.session.commit()
        return jsonify(id=new_task.id, title=new_task.title, status=new_task.status, column=new_task.column)
    else:
        tasks = Task.query.all()
        return jsonify([{
            'id': task.id,
            'title': task.title,
            'status': task.status,
            'completion_date': str(task.completion_date) if task.completion_date else None,
            'column': task.column
        } for task in tasks])

@app.route('/api/tasks/<int:task_id>', methods=['PUT', 'DELETE'])
def update_task(task_id):
    task = Task.query.get_or_404(task_id)
    if request.method == 'PUT':
        data = request.json
        if 'title' in data and not data['title'].strip():
            # If the title is empty, delete the task
            db.session.delete(task)
            db.session.commit()
            return jsonify({'success': True, 'deleted': True})
        else:
            task.title = data.get('title', task.title)
            task.status = data.get('status', task.status)
            task.column = data.get('column', task.column)
            if data.get('status') == 'finished':
                task.completion_date = date.today()
            else:
                task.completion_date = None
            db.session.commit()
            return jsonify({'success': True, 'deleted': False})
    elif request.method == 'DELETE':
        db.session.delete(task)
        db.session.commit()
        return jsonify({'success': True})
    
@app.route('/api/tasks/archive', methods=["POST"])
def archive_completed_tasks():
    tasks = Task.query.filter(Task.status == 'finished')
    for task in tasks:
        task.status = 'archived'
    db.session.commit()
    return jsonify({'success': True})

@app.route('/api/habits', methods=['GET', 'POST'])
def handle_habits():
    '''
    POST request: Creates a new habit in db and returns related JSON
    GET  request: Returns a JSON of all habits and related fields
    '''
    if request.method == 'POST':
        data = request.json
        new_habit = Habit(name=data['name'], days=','.join(data.get('days', [])))
        db.session.add(new_habit)
        db.session.commit()
        return jsonify(id=new_habit.id, name=new_habit.name, days=new_habit.days.split(','))
    else:
        habits = Habit.query.all()
        return jsonify([{
            'id': habit.id,
            'name': habit.name,
            'days': habit.days.split(',') if habit.days else []
        } for habit in habits])
        
@app.route('/api/tasks/order', methods=['PUT'])
def update_task_order():
    data = request.json
    task_id = data.get('taskId')
    new_order = data.get('newOrder')
    new_status = data.get('newStatus')
    
    if not task_id or new_order is None:
        return jsonify({'success': False, 'error': 'Missing taskId or newOrder'}), 400
    
    try:
        task = Task.query.get(task_id)
        if task:
            if new_status and new_status != task.status:
                # If the status has changed, move the task to the end of the new status
                max_order = db.session.query(db.func.max(Task.order)).filter(Task.status == new_status).scalar() or 0
                task.order = max_order + 1
                task.status = new_status
            else:
                # Otherwise, just update the order
                task.order = new_order
            
            db.session.commit()
            return jsonify({'success': True})
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'error': str(e)}), 500

@app.route('/api/habits/<int:habit_id>', methods=['PUT', 'DELETE'])
def update_habit(habit_id):
    '''
    PUT    request: Change Habit name and days
    DELETE request: Delete the habit
    '''
    try:
        habit = Habit.query.get_or_404(habit_id)
        if request.method == 'PUT':
            data = request.json
            if 'name' in data:
                habit.name = data['name']
            if 'days' in data:
                habit.days = ','.join(map(str, data['days']))
            if 'toggle_date' in data:
                toggle_date = date.fromisoformat(data['toggle_date'])
                existing_date = HabitDate.query.filter_by(habit_id=habit.id, date=toggle_date).first()
                if existing_date:
                    db.session.delete(existing_date)
                else:
                    new_date = HabitDate(habit_id=habit.id, date=toggle_date)
                    db.session.add(new_date)
            db.session.commit()
            return jsonify({'success': True})
        elif request.method == 'DELETE':
            db.session.delete(habit)
            db.session.commit()
            return jsonify({'success': True})
    except Exception as e:
        app.logger.error(f"An error occurred while updating habit {habit_id}: {str(e)}")
        return jsonify({'success': False, 'error': str(e)}), 500

@app.route('/api/journal', methods=['GET', 'POST'])
def handle_journal():
    if request.method == 'POST':
        data = request.json
        new_entry = JournalEntry(
            title=data['title'],
            content=data['content'],
            date=date.today(),
            ai_comment=data.get('ai_comment', '')
        )
        db.session.add(new_entry)
        db.session.commit()
        return jsonify(id=new_entry.id, title=new_entry.title, content=new_entry.content, date=str(new_entry.date), ai_comment=new_entry.ai_comment)
    else:
        entries = JournalEntry.query.all()
        return jsonify([{
            'id': entry.id,
            'title': entry.title,
            'content': entry.content,
            'date': str(entry.date),
            'ai_comment': entry.ai_comment
        } for entry in entries])

@app.route('/api/journal/<int:entry_id>', methods=['PUT', 'DELETE'])
def update_journal(entry_id):
    entry = JournalEntry.query.get_or_404(entry_id)
    if request.method == 'PUT':
        data = request.json
        entry.title = data.get('title', entry.title)
        entry.content = data.get('content', entry.content)
        entry.ai_comment = data.get('ai_comment', entry.ai_comment)
        db.session.commit()
        return jsonify({'success': True})
    elif request.method == 'DELETE':
        db.session.delete(entry)
        db.session.commit()
        return jsonify({'success': True})

@app.route('/api/history', methods=['GET'])
def get_yearly_data():
    habits = Habit.query.all()
    tasks = Task.query.filter(Task.completion_date.isnot(None)).all()
    journal_entries = JournalEntry.query.all()
    
    yearly_data = {}
    
    for habit in habits:
        habit_dates = HabitDate.query.filter_by(habit_id=habit.id).all()
        for habit_date in habit_dates:
            date_str = str(habit_date.date)
            if date_str not in yearly_data:
                yearly_data[date_str] = {"completedHabits": [], "completedTasks": [], "journalEntries": []}
            yearly_data[date_str]["completedHabits"].append(habit.name)
    
    for task in tasks:
        date_str = str(task.completion_date)
        if date_str not in yearly_data:
            yearly_data[date_str] = {"completedHabits": [], "completedTasks": [], "journalEntries": []}
        yearly_data[date_str]["completedTasks"].append(task.title)
    
    for entry in journal_entries:
        date_str = str(entry.date)
        if date_str not in yearly_data:
            yearly_data[date_str] = {"completedHabits": [], "completedTasks": [], "journalEntries": []}
        yearly_data[date_str]["journalEntries"].append(entry.title)
    
    for date_str, data in yearly_data.items():
        total_habits = len(habits)
        completed_habits = len(data["completedHabits"])
        data["completionRate"] = completed_habits / total_habits if total_habits > 0 else 0
    
    # Return the data with date strings as keys
    return jsonify(yearly_data)

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    app.run(debug=True)