Code
unknown
plain_text
2 years ago
62 kB
14
Indexable
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Finance Tracker</title> <link href="static/assets/css/feather-icons.css" rel="stylesheet"> <link href="static/assets/css/style.css" rel="stylesheet"> <style> /* Import Nexa Bold font from an online source */ @import url('https://fonts.googleapis.com/css2?family=Nexa:wght@700&display=swap'); @import url('https://cdn-uicons.flaticon.com/uicons-regular-rounded/css/uicons-regular-rounded.css'); </style> <script src="https://cdn.jsdelivr.net/npm/apexcharts@3.27.3/dist/apexcharts.min.js"></script> </head> <body> <div id="myModal" class="modal"> <div class="modal-content"> <span class="close">×</span> <h1>Edit Row</h1> <div class="modal-body"> <div class="input-group"> <label for="nameInput">Name</label> <input type="text" id="nameInput" > </div> <div class="input-group"> <label for="incomeInput">Income</label> <input type="text" id="incomeInput" > </div> <div class="input-group"> <label for="accountInput">Bank Account</label> <input type="text" id="accountInput" > </div> <div class="input-group"> <label for="sourceInput">Income Source</label> <input type="text" id="sourceInput" > </div> <div class="input-group"> <label for="dateInput">Date</label> <input type="text" id="dateInput"> </div> </div> <div id="addModal" style="display: none; position: absolute; top: 100%; left: 0; width: 100%;"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="modalTitle">Add Expense</h5> <span class="close">×</span> </div> <div class="modal-body"> <form> <div class="form-group"> <label for="nameInput">Name:</label> <input type="text" class="form-control" id="newSourceInput"> </div> <div class="form-group"> <label for="descriptionInput">Description:</label> <input type="text" class="form-control" id="descriptionInput"> </div> </form> </div> <div class="modal-footer"> <div class="button-container"> <button type="button" class="btn btn-secondary close">Close</button> <button type="button" class="btn btn-primary" id = "save-btn">Save</button> </div> </div> </div> </div> <button id="addIncomeButton" style="display: none;"> <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" class="bi bi-chevron-down" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z"/> </svg>Add New Income Source</button> <button id="addExpenseButton" style="display: none;"> <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" class="bi bi-chevron-down" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z"/> </svg>Add New Expense Source</button> </div> </div> <div class="container"> <h1 style="font-size: 38px; margin-bottom: 0;">Finance Tracker</h1> <div class="outline-box"> <i class="fi fi-rr-chart-line-up"></i> <span class="overview-text">Overview</span> <div class="card-container"> <div class="card"></div> <div class="card"></div> <div class="chart-container"> <canvas id="line-chart"></canvas> </div> </div> </div> <div class="outline-box"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" class="bi bi-wallet" viewBox="0 0 16 16"> <path d="M0 3a2 2 0 0 1 2-2h13.5a.5.5 0 0 1 0 1H15v2a1 1 0 0 1 1 1v8.5a1.5 1.5 0 0 1-1.5 1.5h-12A2.5 2.5 0 0 1 0 12.5V3zm1 1.732V12.5A1.5 1.5 0 0 0 2.5 14h12a.5.5 0 0 0 .5-.5V5H2a1.99 1.99 0 0 1-1-.268zM1 3a1 1 0 0 0 1 1h12V2H2a1 1 0 0 0-1 1z"/> </svg> <span class="overview-text">Finances</span> <!-- Buttons inside the Finances outline box --> <div class="tab_box"> <button class="tab_btn"> <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" class="bi bi-plus-circle" viewBox="0 0 16 16"> <path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/> <path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"/> </svg> <span class="text">Today</span> </button> <button class="tab_btn"> <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" class="bi bi-dash-circle" viewBox="0 0 16 16"> <path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/> <path d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8z"/> </svg> <span class="text">Today</span> </button> <button class="tab_btn"> <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" class="bi bi-arrow-up-circle" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-7.5 3.5a.5.5 0 0 1-1 0V5.793L5.354 7.854a.5.5 0 1 1-.708-.708l3-3a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 5.707V11.5z"/> </svg> <span class="text">Income</span> </button> <button class="tab_btn"> <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" class="bi bi-arrow-down-circle" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.5 4.5a.5.5 0 0 0-1 0v5.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V4.5z"/> </svg> <span class="text">Expenses</span> </button> <button class="tab_btn"> <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" class="bi bi-calendar" viewBox="0 0 16 16"> <path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z"/> </svg> <span class="text">Calendar</span> </button> <div class="line"></div> </div> <!-- Content divs inside the Finances outline box --> <div class="content-box"> <div class="content"> <div class="horizontal-line"></div> <table> <thead> <tr> <th> <svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" fill="currentColor" class="bi bi-type" viewBox="0 0 17 17"> <path d="m2.244 13.081.943-2.803H6.66l.944 2.803H8.86L5.54 3.75H4.322L1 13.081h1.244zm2.7-7.923L6.34 9.314H3.51l1.4-4.156h.034zm9.146 7.027h.035v.896h1.128V8.125c0-1.51-1.114-2.345-2.646-2.345-1.736 0-2.59.916-2.666 2.174h1.108c.068-.718.595-1.19 1.517-1.19.971 0 1.518.52 1.518 1.464v.731H12.19c-1.647.007-2.522.8-2.522 2.058 0 1.319.957 2.18 2.345 2.18 1.06 0 1.716-.43 2.078-1.011zm-1.763.035c-.752 0-1.456-.397-1.456-1.244 0-.65.424-1.115 1.408-1.115h1.805v.834c0 .896-.752 1.525-1.757 1.525z"/> </svg> <span class="text">Name</span></th> <th> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-cash" viewBox="0 0 16 16"> <path d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/> <path d="M0 4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V4zm3 0a2 2 0 0 1-2 2v4a2 2 0 0 1 2 2h10a2 2 0 0 1 2-2V6a2 2 0 0 1-2-2H3z"/> </svg><span class="text">Income</span></th> <th><svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" class="bi bi-bank2" viewBox="0 0 16 16"> <path d="M8.277.084a.5.5 0 0 0-.554 0l-7.5 5A.5.5 0 0 0 .5 6h1.875v7H1.5a.5.5 0 0 0 0 1h13a.5.5 0 1 0 0-1h-.875V6H15.5a.5.5 0 0 0 .277-.916l-7.5-5zM12.375 6v7h-1.25V6h1.25zm-2.5 0v7h-1.25V6h1.25zm-2.5 0v7h-1.25V6h1.25zm-2.5 0v7h-1.25V6h1.25zM8 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM.5 15a.5.5 0 0 0 0 1h15a.5.5 0 1 0 0-1H.5z"/> </svg> <span class="text">Bank Account</span></th> <th> <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" class="bi bi-arrow-up-circle" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-7.5 3.5a.5.5 0 0 1-1 0V5.793L5.354 7.854a.5.5 0 1 1-.708-.708l3-3a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 5.707V11.5z"/> </svg><span class="text">Income Source</span> </th> <th class="no-right-border"> <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" class="bi bi-calendar" viewBox="0 0 16 16"> <path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z"/> </svg><span class="text">Date</span></th> </tr> </thead> <tbody> <tr> <td><input type="text" class="input-box" data-column="name"></td> <td><input type="number" class="input-box" data-column="income"></td> <td><input type="text" class="input-box" data-column="bank_account"></td> <td><select id ="dropdown_table" data-column="income_source"></select></td> <td class="no-right-border"><input type="text" class="input-box" data-column="date"></td> </tr> </tbody> </table> <div class="horizontal-line"></div> <div class = "row-add"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-lg" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2Z"/> </svg> <span>New</span> </div> <div class="horizontal-line"></div> </div> <div class="content"> <div class="horizontal-line"></div> <table id ="expense-table"> <thead> <tr> <th> <svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" fill="currentColor" class="bi bi-type" viewBox="0 0 17 17"> <path d="m2.244 13.081.943-2.803H6.66l.944 2.803H8.86L5.54 3.75H4.322L1 13.081h1.244zm2.7-7.923L6.34 9.314H3.51l1.4-4.156h.034zm9.146 7.027h.035v.896h1.128V8.125c0-1.51-1.114-2.345-2.646-2.345-1.736 0-2.59.916-2.666 2.174h1.108c.068-.718.595-1.19 1.517-1.19.971 0 1.518.52 1.518 1.464v.731H12.19c-1.647.007-2.522.8-2.522 2.058 0 1.319.957 2.18 2.345 2.18 1.06 0 1.716-.43 2.078-1.011zm-1.763.035c-.752 0-1.456-.397-1.456-1.244 0-.65.424-1.115 1.408-1.115h1.805v.834c0 .896-.752 1.525-1.757 1.525z"/> </svg> <span class="text">Name</span></th> <th> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-cash" viewBox="0 0 16 16"> <path d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/> <path d="M0 4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V4zm3 0a2 2 0 0 1-2 2v4a2 2 0 0 1 2 2h10a2 2 0 0 1 2-2V6a2 2 0 0 1-2-2H3z"/> </svg><span class="text">Expense</span></th> <th><svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" class="bi bi-bank2" viewBox="0 0 16 16"> <path d="M8.277.084a.5.5 0 0 0-.554 0l-7.5 5A.5.5 0 0 0 .5 6h1.875v7H1.5a.5.5 0 0 0 0 1h13a.5.5 0 1 0 0-1h-.875V6H15.5a.5.5 0 0 0 .277-.916l-7.5-5zM12.375 6v7h-1.25V6h1.25zm-2.5 0v7h-1.25V6h1.25zm-2.5 0v7h-1.25V6h1.25zm-2.5 0v7h-1.25V6h1.25zM8 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM.5 15a.5.5 0 0 0 0 1h15a.5.5 0 1 0 0-1H.5z"/> </svg> <span class="text">Bank Account</span></th> <th> <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" class="bi bi-arrow-down-circle" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.5 4.5a.5.5 0 0 0-1 0v5.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V4.5z"/> </svg> <span class="text">Expense Source</span> </th> <th class="no-right-border"> <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" class="bi bi-calendar" viewBox="0 0 16 16"> <path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z"/> </svg><span class="text">Date</span></th> </tr> </thead> <tbody> <tr> <td><input type="text" class="input-box" data-column="name"></td> <td><input type="text" class="input-box" data-column="income"></td> <td><input type="text" class="input-box" data-column="bank_account"></td> <td><select id ="expense_dropdown" data-column="income_source"></select></td> <td class="no-right-border"><input type="text" class="input-box" data-column="date"></td> </tr> </tbody> </table> <div class="horizontal-line"></div> <div class="row-add-expense"> <button class="expense-btn"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-lg" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2Z"/> </svg>New</button> </div> <div class="horizontal-line"></div> </div> <div class="content"> </div> <div class="content"> </div> <div class="content"> </div> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="static/assets/js/script.js"></script> <script src="static/assets/js/modal.js"></script> <script src="static/assets/js/expense_modal.js"></script> <script src="https://unpkg.com/@themesberg/flowbite@1.2.0/dist/flowbite.bundle.js"></script> </body> </html> /* style.css (Your main CSS file) */ body { padding: 0px; margin-top: 20px; margin-left: 30px; background-color: #121212; color: white; font-family: "Nexa Bold", sans-serif; font-weight: bold; } .container { margin: 0 auto; padding: 50px; display: flex; flex-direction: column; } .overview-text { font-family: "Nexa Bold", sans-serif; font-size: 18px; /* Adjust the font size as needed */ margin-left: 12px; } .outline-box { margin-top: 60px; border: 0.5px solid #6262627e; background-color: transparent; padding: 10px; border-radius: 0.5px; } .small-outline-box { width: 500px; height: 400px; margin-top: 60px; border: 0.5px solid #6262627e; background-color: transparent; padding: 10px; border-radius: 0.5px; } .card-container { display: flex; padding-bottom: 20px; border-radius: 0px; margin-top: 30px; margin-left: 10px; } .card { width: 200px; height: 300px; background: #242323; margin-left: 20px; } .chart-container { margin-left: 15px; flex: 1; height: 450px; } ::-webkit-scrollbar { width: 10px; background-color: #30373d; } ::-webkit-scrollbar-thumb { background-color: #555; border-radius: 5px; } ::-webkit-scrollbar-thumb:hover { background-color: #555; } .tab_box{ width: 100%; display:flex; align-items: left; position: relative; } .tab_btn{ margin-top: 20px; margin-bottom: 40px; margin-right: 10px; font-size: 20px; background: none; border: none; color: white; cursor: pointer; } .line{ position: absolute; top: 50px; left:0; width: 90px; height: 4px; background-color: #30373d; border-radius: 10px; transition: all .3s ease-in-out; } .text{ margin-left: 0px; font-size: 18px; } table { width: 80%; } table th .text { font-size: 16px; font-weight: normal; color: #ffffff; } table th svg { margin-right: 5px; /* Add some right margin between the icon and the text */ } th, td { text-align: left; font-size: 16px; font-weight: normal; padding: 4px; border-right: 1px solid rgba(255, 255, 255, 0.315); } /* Styling for table header */ th { background-color: transparent; } th.no-right-border { border-right: none; } .input-box { border: none; /* Remove the default border */ outline: none; /* Remove the default outline */ font-size: 14px; background: transparent; /* Set the background to transparent */ color: #fff; /* Text color for input boxes */ box-shadow: none; /* Remove the box-shadow */ } tr td:last-child { border-right: none; } tr:hover { background-color: #4e4e4e15; /* Change to the desired color when hovering */ } .horizontal-line { width: 100%; /* Set the width of the line to fill the entire container */ height: 0.5px; /* Set the height of the line */ background-color: rgba(255, 255, 255, 0.219); /* Change the color of the line (you can use any valid CSS color) */ } .row-add { display: flex; /* Adding flex display to align items horizontally */ align-items: center; /* Aligning items vertically in the center */ font-size: 16px; /* Adjust this value to change the font size */ font-weight: normal; padding: 4px; cursor: pointer; color: rgba(99, 99, 99, 0.589); } .row-add:hover{ color: white; } /* Styling for the SVG icon */ .row-add svg { margin-right: 10px; /* Adjust this value to add space between the icon and text */ } .modal { display: none; position: fixed; z-index: 1; right: 0; /* Position from the right side */ top: 0; width: 40%; /* Adjust the width as needed */ height: 100%; overflow: auto; background-color: rgba(0, 0, 0, 0.4); transition: right 0.3s; /* Add a smooth transition effect */ } .modal-content { background-color: #242323; height: 100%; border-radius: 2px; padding: 16px; } .modal-body { margin-top: 5px; display: flex; /* Create a flex container */ flex-direction: column; /* Arrange elements vertically */ } .input-group { display: flex; align-items: center; margin-bottom: 15px; } .input-group label { min-width: 120px; /* Adjust this width as needed */ margin-left: 20px; font-size: 16px; font-weight: normal; } .modal-body input { padding-left: 20px; border: none; /* Remove the default border */ outline: none; /* Remove the default outline */ background: transparent; /* Set the background to transparent */ color: #fff; /* Text color for input boxes */ box-shadow: none; /* Remove the box-shadow */ font-size: 16px; align-items: left; flex: 1; } .close { color: #aaa; float: right; font-size: 24px; font-weight: bold; cursor: pointer; } .modal-content h1 { /* Set the font size */ font-weight: bold; /* Make the text bold */ color: #fff; /* Set the text color to white */ margin-top: 40px; /* Remove the default top margin for <h1> */ margin-bottom: 40px; /* Adjust the bottom margin as needed */ } .expense-btn { background-color: transparent; border: none; outline: none; cursor: pointer; align-items: center; /* Aligning items vertically in the center */ font-size: 16px; /* Adjust this value to change the font size */ font-weight: normal; padding: 4px; align-items: center; font-size: 16px; color: rgba(99, 99, 99, 0.589); } /* Add padding to separate the icon from text */ .expense-btn svg { margin-right: 5px; color: rgba(99, 99, 99, 0.589); } /* Hover effect on the button and the SVG */ .expense-btn:hover { padding: 4px; color:white; } .modal-content button { margin-top: 40px; margin-left: 20px; margin-bottom: 10px; background-color: #0d71bd; border-radius: 3px; color: white; padding: 10px 20px; border: none; cursor: pointer; } /* Additional style for the buttons */ .modal-content button:hover { background-color: #008df9; } .modal-content button svg { margin-right: 5px; vertical-align: middle; /* Added to vertically align the icon with the text */ } #addModal { display: none; /* Hide the modal by default */ background-color: #222222; max-width: 400px; max-height: 200px; margin-left: 36px; border-radius: 3px; } #addModal .modal-header { display: flex; justify-content: space-between; align-items: center; padding: 1rem; background-color: #121316; border-top-left-radius: 3px; border-top-right-radius: 3px; } /* Form labels */ #addModal label { display: block; font-size: 0.9rem; font-weight: 500; color: #333; } #addModal .modal-content{ padding: 0; } /* Form inputs */ #addModal input[type="text"] { width: 100%; border-radius: 4px; } #addModal #newSourceInput.form-control { width: 90%; padding: 0; font-size: 12px; min-height: 36px; background-color: #1d1d1d; color: white; } #addModal #descriptionInput.form-control { padding: 0px; width: 90%; font-size: 12px; min-height: 60px ; background-color: #1d1d1d; color: white; } /* Modal header title */ #addModal .modal-title { margin: 0; font-size: 14px; font-weight: normal; } /* Modal body */ #addModal .modal-body { padding-left: 1rem; padding-top: 1rem; padding-right: 1rem; margin: 0; background-color: #121316; } /* Form group */ #addModal .form-group { margin-bottom: 1rem; } /* Form labels */ #addModal label { margin-top: 20px; margin-bottom: 10px; display: block; font-size: 0.9rem; font-weight: 500; color: #ffffff; } /* Form inputs */ #addModal input[type="text"] { width: 100%; padding: 0.5rem 1rem; border-radius: 4px; } /* Close button */ #addModal .close { font-size: 1.2rem; color: #333; cursor: pointer; } #addModal .modal-footer { display: flex; justify-content: flex-end; align-items: center; /* Vertically align the buttons in the center */ background-color: #121316; max-height: 50px; overflow: hidden; /* Hide the overflowing content */ } .button-container { display: flex; flex-wrap: nowrap; /* Prevent button wrapping */ } #addModal .btn { margin-bottom: 30px;/* Add some space between buttons */ padding: 5px 10px; /* Adjust padding to make buttons smaller */ max-width: 100px; /* Limit the width of the buttons */ white-space: nowrap; /* Prevent button text wrapping */ } #addModal .btn-primary { margin-right: 50px; background-color: #0d71bd; color: white; border: none; font-size: 12px; cursor: pointer; } #addModal .btn-primary:hover { background-color: #008df9; /* Change the background color on hover */ } #addModal .btn-secondary { font-weight: normal; background-color: transparent; font-size: 12px; color: #ffffff67; border: none; cursor: pointer; } #addModal .btn-secondary:hover { color: white; /* Change the background color on hover */ } from flask import Flask, render_template, request, jsonify from flask import Blueprint import pyodbc app = Flask(__name__) @app.route('/') def index(): return render_template('index.html') @app.route('/get_data', methods=['GET']) def get_data(): try: conn = pyodbc.connect('DRIVER={SQL Server};SERVER=SOHAIL_LAPTOP;DATABASE=FTrackDB;UID=sa;PWD=sohail2004') cursor = conn.cursor() cursor = conn.cursor() cursor.execute("SELECT * FROM INCOME") data = cursor.fetchall() columns = ['Name', 'Income', 'BankAccount', 'IncomeSource', 'Date'] rows = [{col: row[i] for i, col in enumerate(columns)} for row in data] return jsonify(rows) except Exception as e: return jsonify({"error": str(e)}) @app.route('/add_row', methods=['POST']) def add_row(): try: data = request.json name = data.get('name') income = data.get('income') bank_account = data.get('bank_account') income_source = data.get('income_source') date = data.get('date') conn = pyodbc.connect('DRIVER={SQL Server};SERVER=SOHAIL_LAPTOP;DATABASE=FTrackDB;UID=sa;PWD=sohail2004') cursor = conn.cursor() cursor = conn.cursor() cursor.execute("INSERT INTO INCOME (Name, Income, BankAccount, IncomeSource, Date,user_name) " "VALUES (?, ?, ?, ?, ?,'Test')", (name, income, bank_account, income_source, date)) conn.commit() return jsonify({"message": "Row added successfully!"}) except Exception as e: return jsonify({"error": str(e)}) @app.route('/get_expense_data', methods=['GET']) def get_expense_data(): try: conn = pyodbc.connect('DRIVER={SQL Server};SERVER=SOHAIL_LAPTOP;DATABASE=FTrackDB;UID=sa;PWD=sohail2004') cursor = conn.cursor() cursor.execute("SELECT * FROM EXPENSE") # Assuming the "EXPENSE" table contains the data for the Expense table data = cursor.fetchall() columns = ['Name', 'Expense', 'BankAccount', 'ExpenseSource', 'Date'] # Adjust the column names as per your actual table rows = [{col: row[i] for i, col in enumerate(columns)} for row in data] return jsonify(rows) except Exception as e: return jsonify({"error": str(e)}) @app.route('/add_expense_row', methods=['POST']) def add_expense_row(): try: data = request.json name = data.get('name') expense = data.get('expense') bank_account = data.get('bank_account') expense_source = data.get('expense_source') # Assuming the column name is different in the Expense table date = data.get('date') conn = pyodbc.connect('DRIVER={SQL Server};SERVER=SOHAIL_LAPTOP;DATABASE=FTrackDB;UID=sa;PWD=sohail2004') cursor = conn.cursor() cursor.execute("INSERT INTO EXPENSE (Name, Expense, BankAccount, ExpenseSource, Date,user_name) " "VALUES (?, ?, ?, ?, ?,'Test')", (name, expense, bank_account, expense_source, date)) # Adjust the column names as per your actual table conn.commit() return jsonify({"message": "Row added to the Expense table successfully!"}) except Exception as e: return jsonify({"error": str(e)}) @app.route('/add_expense_source', methods=['POST']) def add_expense_source(): try: data = request.json new_source_name = data.get('newSourceName') description = data.get('description') conn = pyodbc.connect('DRIVER={SQL Server};SERVER=SOHAIL_LAPTOP;DATABASE=FTrackDB;UID=sa;PWD=sohail2004') cursor = conn.cursor() cursor.execute("INSERT INTO ExpenseSources (source_name, description) VALUES (?, ?)", (new_source_name, description)) conn.commit() return jsonify({"message": "New expense source added successfully!"}) except Exception as e: return jsonify({"error": str(e)}) @app.route('/add_income_source', methods=['POST']) def add_income_source(): try: data = request.json new_source_name = data.get('newSourceName') description = data.get('description') conn = pyodbc.connect('DRIVER={SQL Server};SERVER=SOHAIL_LAPTOP;DATABASE=FTrackDB;UID=sa;PWD=sohail2004') cursor = conn.cursor() cursor.execute("INSERT INTO IncomeSources (source_name, description) VALUES (?, ?)", (new_source_name, description)) conn.commit() return jsonify({"message": "New income source added successfully!"}) except Exception as e: return jsonify({"error": str(e)}) @app.route('/get_income_sources', methods=['GET']) def get_income_sources(): try: conn = pyodbc.connect('DRIVER={SQL Server};SERVER=SOHAIL_LAPTOP;DATABASE=FTrackDB;UID=sa;PWD=sohail2004') cursor = conn.cursor() cursor.execute("SELECT source_name FROM IncomeSources") income_sources = [row[0] for row in cursor.fetchall()] return jsonify({"income_sources": income_sources}) except Exception as e: return jsonify({"error": str(e)}) @app.route('/get_expense_sources', methods=['GET']) def get_expense_sources(): try: conn = pyodbc.connect('DRIVER={SQL Server};SERVER=SOHAIL_LAPTOP;DATABASE=FTrackDB;UID=sa;PWD=sohail2004') cursor = conn.cursor() cursor.execute("SELECT source_name FROM ExpenseSources") expense_sources = [row[0] for row in cursor.fetchall()] return jsonify({"expense_sources": expense_sources}) # Corrected variable name except Exception as e: return jsonify({"error": str(e)}) if __name__ == '__main__': app.run(debug=True, host='192.168.0.107', port=5000) #app.run(debug=True, host='192.168.1.108', port=5000) //script 1 document.addEventListener("DOMContentLoaded", function () { var ctx = document.getElementById("line-chart").getContext('2d'); var options = { type: "line", data: { labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep"], datasets: [ { label: 'Sample Series', data: [35, 40, 35, 50, 49, 60, 70, 91, 110], fill: true, borderColor: '#2196f3', backgroundColor: 'rgba(33, 150, 243, 0.2)', borderWidth: 2, pointRadius: 5, pointBackgroundColor: '#2196f3', pointBorderColor: '#ffffff', pointBorderWidth: 2, } ] }, options: { responsive: true, maintainAspectRatio: true, aspectRatio: 2, // Set the desired aspect ratio (height/width) for the chart scales: { x: { grid: { display: true, drawBorder: true, // Display vertical grid lines at chart borders drawTicks: false, // Hide the ticks on the X-axis color: 'rgba(255, 255, 255, 0.1)', lineWidth: 1, } }, y: { grid: { display: true, drawBorder: true, // Display horizontal grid lines at chart borders drawTicks: false, // Hide the ticks on the Y-axis color: 'rgba(255, 255, 255, 0.1)', lineWidth: 1, } } }, plugins: { legend: { display: false } }, layout: { padding: { top: 10, right: 15, bottom: 15, left: 15 } } } }; var chart = new Chart(ctx, options); // Get all tab buttons and content div const tabButtons = document.querySelectorAll(".tab_btn"); const contentDivs = document.querySelectorAll(".content"); const line = document.querySelector('.line'); // Function to hide all content divs function hideContent() { contentDivs.forEach(function (content) { content.style.display = "none"; }); } function showContent(index) { // Hide all content divs hideContent(); // Show the selected content div contentDivs[index].style.display = "block"; } // Initially hide all content divs except the first one hideContent(); showContent(0); // Add click event listener to each tab button tabButtons.forEach(function (button, index) { button.addEventListener("click", function () { // Remove the "active" class from all buttons and content divs tabButtons.forEach(function (btn) { btn.classList.remove("active"); }); contentDivs.forEach(function (content) { content.classList.remove("active"); }); // Add the "active" class to the clicked button and content div button.classList.add("active"); showContent(index); const buttonOffsetLeft = button.offsetLeft; const buttonWidth = button.offsetWidth; line.style.left = buttonOffsetLeft + "px"; line.style.width = buttonWidth + "px"; }); }); async function createNewRow() { const table = document.querySelector('table'); const newRow = document.createElement('tr'); const columns = ['name', 'income', 'bank_account', 'income_source', 'date']; // Fetch income sources and populate the dropdown options const response = await fetchIncomeSources(); const incomeSources = response.income_sources; console.log(incomeSources); const select = document.createElement('select'); select.id = 'dropdown_table'; select.classList.add('input-box'); select.dataset.column = 'income_source'; // Set the column to identify the selected value // Add an empty option as the default const emptyOption = document.createElement('option'); emptyOption.value = ''; emptyOption.textContent = 'Select Income Source'; select.appendChild(emptyOption); // Populate the dropdown with income source options incomeSources.forEach(incomeSource => { const option = document.createElement('option'); option.value = incomeSource; option.textContent = incomeSource; select.appendChild(option); }); columns.forEach(column => { const cell = document.createElement('td'); // Check if it's the income_source column and add the select dropdown if (column === 'income_source') { cell.appendChild(select); } else { const input = document.createElement('input'); input.type = 'text'; input.classList.add('input-box'); input.dataset.column = column; cell.appendChild(input); } newRow.appendChild(cell); }); // Add a blur event listener to each input field columns.forEach(col => { const input = newRow.querySelector(`[data-column="${col}"]`); input.addEventListener('blur', function () { const rowData = {}; let allFieldsFilled = true; // Variable to check if all fields are filled columns.forEach(col => { const input = newRow.querySelector(`[data-column="${col}"]`); rowData[col] = input.value; if (!input.value) { allFieldsFilled = false; } }); if (allFieldsFilled) { saveNewRowData(rowData); } else { console.log('Please fill all required fields.'); } }); }); table.querySelector('tbody').appendChild(newRow); } // Function to fetch income sources from the server async function fetchIncomeSources() { try { const response = await fetch('/get_income_sources'); const data = await response.json(); if (data && data.income_sources && Array.isArray(data.income_sources)) { return data.income_sources; // Return the array of income sources } else { console.error('Received invalid income sources data from the server.'); return []; // Return an empty array if data format is invalid } } catch (error) { console.error('Error fetching income sources:', error); return []; // Return an empty array on error } } async function fetchExpenseSources() { try { const response = await fetch('/get_expense_sources'); const data = await response.json(); if (data && data.expense_sources && Array.isArray(data.expense_sources)) { return data.expense_sources; // Return the array of income sources } else { console.error('Received invalid expense sources data from the server.'); return []; // Return an empty array if data format is invalid } } catch (error) { console.error('Error fetching expense sources:', error); return []; // Return an empty array on error } } function fetchAndPopulateExpenseTable() { fetch('/get_expense_data') // Replace '/get_expense_data' with the appropriate endpoint that retrieves data for the Expense table .then(response => response.json()) .then(data => { const table = document.getElementById('expense-table'); const tbody = table.querySelector('tbody'); tbody.innerHTML = ''; // Clear existing rows data.forEach(rowData => { const newRow = document.createElement('tr'); const columns = ['Name', 'Expense', 'BankAccount', 'ExpenseSource', 'Date']; // Assuming the column names in the Expense table are different from the Income table columns.forEach(column => { const cell = document.createElement('td'); const input = document.createElement('input'); input.type = 'text'; input.classList.add('input-box'); input.dataset.column = column; input.value = rowData[column]; // Access the data using the column key input.disabled = true; cell.appendChild(input); newRow.appendChild(cell); }); tbody.appendChild(newRow); }); }) .catch(error => console.error('Error fetching data for Expense table:', error)); } // Call this function when the page loads or when switching to the Expense tab fetchAndPopulateExpenseTable(); function fetchDataAndPopulateTable() { fetch('/get_data') .then(response => response.json()) .then(data => { // Verify that the data is received correctly console.log(data); const table = document.querySelector('table'); const tbody = table.querySelector('tbody'); tbody.innerHTML = ''; // Clear existing rows data.forEach(rowData => { const newRow = document.createElement('tr'); const columns = ['Name', 'Income', 'BankAccount', 'IncomeSource', 'Date']; columns.forEach(column => { const cell = document.createElement('td'); const input = document.createElement('input'); input.type = 'text'; input.classList.add('input-box'); input.dataset.column = column; input.value = rowData[column]; input.disabled = true;// Access the data using the column key cell.appendChild(input); newRow.appendChild(cell); }); tbody.appendChild(newRow); }); }) .catch(error => console.error('Error fetching data:', error)); } function saveNewRowData(rowData) { console.log("Data to be sent to the server:", rowData); // Retrieve the selected income source from the dropdown const incomeSourceDropdown = document.querySelector('select[data-column="income_source"]'); rowData['income_source'] = incomeSourceDropdown.value; fetch('/add_row', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(rowData) }) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error saving data:', error)); } fetchDataAndPopulateTable(); async function createNewExpenseRow() { const table = document.getElementById('expense-table'); const newRow = document.createElement('tr'); const columns = ['name', 'expense', 'bank_account', 'expense_source', 'date']; // Fetch expense sources and populate the dropdown options const expenseSources = await fetchExpenseSources(); console.log(expenseSources); const select = document.createElement('select'); select.id = 'expense_dropdown'; select.classList.add('input-box'); select.dataset.column = 'expense_source'; // Set the column to identify the selected value const emptyOption = document.createElement('option'); emptyOption.value = ''; // Set the value of the option to an empty string emptyOption.textContent = 'Select Expense Source'; // Set the displayed text select.appendChild(emptyOption); // Populate the dropdown with expense source options expenseSources.forEach(expenseSource => { const option = document.createElement('option'); option.value = expenseSource; option.textContent = expenseSource; select.appendChild(option); }); columns.forEach(column => { const cell = document.createElement('td'); const input = document.createElement('input'); input.type = 'text'; input.classList.add('input-box'); input.dataset.column = column; // Check if it's the expense_source column and add the select dropdown if (column === 'expense_source') { cell.appendChild(select); } else { cell.appendChild(input); } newRow.appendChild(cell); }); // Add a blur event listener to each input field columns.forEach(col => { const input = newRow.querySelector(`[data-column="${col}"]`); input.addEventListener('blur', function () { const rowData = {}; let allFieldsFilled = true; // Variable to check if all fields are filled columns.forEach(col => { const input = newRow.querySelector(`[data-column="${col}"]`); rowData[col] = input.value; if (!input.value) { allFieldsFilled = false; } }); // Update the expense_source value in rowData with the selected dropdown option rowData['expense_source'] = select.value; if (allFieldsFilled) { // Save the new expense row data to the database saveNewExpenseRowData(rowData); } else { console.log('Please fill all required fields.'); } }); }); table.querySelector('tbody').appendChild(newRow); } function saveNewExpenseRowData(rowData) { console.log("Data to be sent to the server:", rowData); fetch('/add_expense_row', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(rowData) }) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error saving data:', error)); } async function fetchIncomeSources() { try { const response = await fetch('/get_income_sources'); const data = await response.json(); return data; } catch (error) { console.error('Error fetching income sources:', error); return []; } } // Call this function when clicking the "New" button in the Expense section document.querySelector('.expense-btn').addEventListener('click', function () { createNewExpenseRow(); }); // Add click event listener to the "New" button document.querySelector('.row-add').addEventListener('click', function () { createNewRow(); }); }); //script2 // Get the modal element const modal = document.getElementById("myModal"); const addModal = document.getElementById("addModal"); const modalTitleElement = document.getElementById("modalTitle"); // Get the first table element const table = document.querySelector("table"); // Get the second table element const expenseTable = document.getElementById("expense-table"); // Variable to keep track of the current row index being edited let currentRowIndex = null; function addEventListenersForButtons() { const addExpenseButton = document.getElementById("addExpenseButton"); const addIncomeButton = document.getElementById("addIncomeButton"); addExpenseButton.addEventListener("click", onAddExpenseButtonClick); addIncomeButton.addEventListener("click", onAddIncomeButtonClick); } function removeEventListenersForButtons() { const addExpenseButton = document.getElementById("addExpenseButton"); const addIncomeButton = document.getElementById("addIncomeButton"); addExpenseButton.removeEventListener("click", onAddExpenseButtonClick); addIncomeButton.removeEventListener("click", onAddIncomeButtonClick); } // Function to handle the click event for adding an expense function onAddExpenseButtonClick() { const buttonRect = addExpenseButton.getBoundingClientRect(); addModal.style.display = "block"; addModal.style.position = "absolute"; addModal.style.top = buttonRect.bottom + "px"; modalTitleElement.textContent = "Add Expense Source"; // Change title for expense table const addCloseBtn = document.querySelector("#addModal .close"); addCloseBtn.addEventListener("click", function () { addModal.style.display = "none"; addCloseBtn.removeEventListener("click", onAddExpenseButtonClick); // Remove the event listener window.removeEventListener("click", onCloseButtonClick); // Remove the click event listener for closing the modal }); // Attach a click event to the window to close the modal when clicked outside window.addEventListener("click", onCloseButtonClick); } // Function to handle the click event for adding an income function onAddIncomeButtonClick() { const buttonRect = addIncomeButton.getBoundingClientRect(); addModal.style.display = "block"; addModal.style.position = "absolute"; addModal.style.top = buttonRect.bottom + "px"; modalTitleElement.textContent = "Add Income Source"; // Change title for income table document.getElementById("nameInput").value = ""; document.getElementById("descriptionInput").value = ""; const addCloseBtn = document.querySelector("#addModal .close"); addCloseBtn.addEventListener("click", function () { addModal.style.display = "none"; addCloseBtn.removeEventListener("click", onAddIncomeButtonClick); // Remove the event listener window.removeEventListener("click", onCloseButtonClick); // Remove the click event listener for closing the modal }); // Attach a click event to the window to close the modal when clicked outside window.addEventListener("click", onCloseButtonClick); } // Function to handle the click event for closing the modal function onCloseButtonClick(event) { if (event.target === modal || event.target === addModal) { modal.style.display = "none"; addModal.style.display = "none"; // When the modal is closed, remove the event listeners const addCloseBtn = document.querySelector("#addModal .close"); addCloseBtn.removeEventListener("click", onAddExpenseButtonClick); addCloseBtn.removeEventListener("click", onAddIncomeButtonClick); window.removeEventListener("click", onCloseButtonClick); } } // Function to open the modal and set the input values based on the clicked row function openModalWithData(row, clickedCell) { const name = row.cells[0].querySelector("input").value; const income = row.cells[1].querySelector("input").value; const account = row.cells[2].querySelector("input").value; const source = row.cells[3].querySelector("input").value; const date = row.cells[4].querySelector("input").value; document.getElementById("nameInput").value = name; document.getElementById("incomeInput").value = income; document.getElementById("accountInput").value = account; document.getElementById("sourceInput").value = source; document.getElementById("dateInput").value = date; removeEventListenersForButtons(); const incomeLabel = document.querySelector('label[for="incomeInput"]'); const sourceLabel = document.querySelector('label[for="sourceInput"]'); const addExpenseButton = document.getElementById("addExpenseButton"); const addIncomeButton = document.getElementById("addIncomeButton"); // Remove any previous event listeners before adding new ones addExpenseButton.removeEventListener("click", onAddExpenseButtonClick); addIncomeButton.removeEventListener("click", onAddIncomeButtonClick); if ( row.parentElement === expenseTable.querySelector("tbody") && clickedCell.cellIndex === 3 ) { addExpenseButton.style.display = "block"; addIncomeButton.style.display = "none"; // Hide addIncomeButton addExpenseButton.addEventListener("click", onAddExpenseButtonClick); const saveButton = document.getElementById("save-btn"); saveButton.addEventListener("click", function () { // Get the values from the input fields console.log("Pressed this button") const newSourceName = document.getElementById("newSourceInput").value; const sourceDescription = document.getElementById("descriptionInput").value; // Create a JSON object with the data to send to the server const newIncomeSourceData = { newSourceName: newSourceName, description: sourceDescription, }; // Send the data to the server using AJAX POST request fetch("/add_expense_source", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(newIncomeSourceData), }) .then((response) => response.json()) .then((data) => { console.log(data); // You can handle the server response here addModal.style.display = "none"; // Close the modal // Optionally, you can update the frontend to display the new income source in the list // without having to refresh the page by adding the new data to the existing income sources list. }) .catch((error) => { console.error("Error:", error); // Handle the error, display a message, etc. }); }); } else if ( row.parentElement === table.querySelector("tbody") && clickedCell.cellIndex === 3 ) { addIncomeButton.style.display = "block"; addExpenseButton.style.display = "none"; // Hide addExpenseButton const saveButton = document.getElementById("save-btn"); saveButton.addEventListener("click", function () { // Get the values from the input fields console.log("Pressed this button") const newSourceName = document.getElementById("newSourceInput").value; const sourceDescription = document.getElementById("descriptionInput").value; // Create a JSON object with the data to send to the server const newIncomeSourceData = { newSourceName: newSourceName, description: sourceDescription, }; // Send the data to the server using AJAX POST request fetch("/add_income_source", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(newIncomeSourceData), }) .then((response) => response.json()) .then((data) => { console.log(data); // You can handle the server response here addModal.style.display = "none"; // Close the modal // Optionally, you can update the frontend to display the new income source in the list // without having to refresh the page by adding the new data to the existing income sources list. }) .catch((error) => { console.error("Error:", error); // Handle the error, display a message, etc. }); }); addIncomeButton.addEventListener("click", onAddIncomeButtonClick); } else { addExpenseButton.style.display = "none"; addIncomeButton.style.display = "none"; } // Check if the clicked row is part of the expense table if (row.parentElement === expenseTable.querySelector("tbody")) { incomeLabel.textContent = "Expense"; sourceLabel.textContent = "Expense Source"; } else { // Set the label text back to "Income" incomeLabel.textContent = "Income"; sourceLabel.textContent = "Income Source"; } modal.style.display = "block"; currentRowIndex = row.rowIndex; } // Add a double-click event listener to the first table using event delegation table.addEventListener("dblclick", function (event) { const targetCell = event.target.closest("td"); if (targetCell) { const row = targetCell.parentElement; openModalWithData(row, targetCell); } }); // Add a double-click event listener to the second table using event delegation expenseTable.addEventListener("dblclick", function (event) { const targetCell = event.target.closest("td"); if (targetCell) { const row = targetCell.parentElement; openModalWithData(row, targetCell); } }); // Get the close button element and add a click event listener to close the modal const closeBtn = document.getElementsByClassName("close")[0]; closeBtn.addEventListener("click", function () { modal.style.display = "none"; addModal.style.display = "none"; }); // When the user clicks anywhere outside the modal, close it window.addEventListener("click", onCloseButtonClick);
Editor is loading...