new fom of MAT credit add in table formate and update show
This commit is contained in:
146
static/css/mat_credit.css
Normal file
146
static/css/mat_credit.css
Normal file
@@ -0,0 +1,146 @@
|
||||
/* ===== CONTAINER ===== */
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 30px auto;
|
||||
padding: 25px;
|
||||
background: #ffffff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 18px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* ===== TABLE ===== */
|
||||
#matTable {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ===== HEADER ===== */
|
||||
#matTable thead th {
|
||||
background: linear-gradient(135deg, #0d6efd, #0a58ca);
|
||||
color: #ffffff;
|
||||
padding: 12px 8px;
|
||||
border: 1px solid #0a58ca;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ===== BODY CELLS ===== */
|
||||
#matTable tbody td {
|
||||
padding: 8px;
|
||||
border: 1px solid #dcdcdc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
/* ===== FY COLUMN ===== */
|
||||
#matTable tbody td:first-child {
|
||||
font-weight: 600;
|
||||
background-color: #f5f8ff;
|
||||
}
|
||||
|
||||
/* ===== INPUT FIELDS ===== */
|
||||
#matTable input {
|
||||
width: 100%;
|
||||
padding: 6px 6px;
|
||||
border: 1px solid #cfd8dc;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
text-align: right;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* TEXT INPUT (Unutilized) */
|
||||
#matTable input[type="text"] {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* INPUT FOCUS */
|
||||
#matTable input:focus {
|
||||
border-color: #0d6efd;
|
||||
box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.15);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* ===== ACTION BUTTON ===== */
|
||||
#matTable button {
|
||||
background-color: #198754;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 6px 14px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
#matTable button:hover {
|
||||
background-color: #157347;
|
||||
}
|
||||
|
||||
/* ===== SAVE ALL BUTTON ===== */
|
||||
button[onclick="saveAll()"] {
|
||||
display: block;
|
||||
width: 300px;
|
||||
margin: 25px auto 0;
|
||||
background-color: #28a745;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button[onclick="saveAll()"]:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
|
||||
/* ===== ROW HOVER ===== */
|
||||
#matTable tbody tr:hover {
|
||||
background-color: #f1f6ff;
|
||||
}
|
||||
|
||||
/* ===== ERROR HIGHLIGHT ===== */
|
||||
.input-error {
|
||||
border-color: #dc3545 !important;
|
||||
background-color: #fff5f5;
|
||||
}
|
||||
|
||||
/* ===== SUCCESS HIGHLIGHT ===== */
|
||||
.row-saved {
|
||||
background-color: #e9f7ef !important;
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE ===== */
|
||||
@media (max-width: 768px) {
|
||||
#matTable {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#matTable thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#matTable tbody tr {
|
||||
display: block;
|
||||
margin-bottom: 15px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#matTable tbody td {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 6px 8px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#matTable tbody td::before {
|
||||
content: attr(data-label);
|
||||
font-weight: 600;
|
||||
color: #0d6efd;
|
||||
}
|
||||
}
|
||||
184
static/js/mat_credit.js
Normal file
184
static/js/mat_credit.js
Normal file
@@ -0,0 +1,184 @@
|
||||
/* =====================================================
|
||||
GLOBAL STATE
|
||||
===================================================== */
|
||||
let addedYears = [];
|
||||
|
||||
/* =====================================================
|
||||
INITIALIZE YEARS FROM DATABASE HEADERS
|
||||
===================================================== */
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const headers = document.querySelectorAll("#tableHeader th");
|
||||
|
||||
headers.forEach(th => {
|
||||
if (th.innerText.startsWith("Utilized")) {
|
||||
const year = th.innerText.replace("Utilized", "").trim();
|
||||
if (!addedYears.includes(year)) {
|
||||
addedYears.push(year);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* =====================================================
|
||||
ADD YEAR COLUMN (DYNAMIC)
|
||||
===================================================== */
|
||||
function addYearColumn() {
|
||||
const yearSelect = document.getElementById("yearSelect");
|
||||
const year = yearSelect.value;
|
||||
|
||||
if (!year) {
|
||||
alert("Please select AY");
|
||||
return;
|
||||
}
|
||||
|
||||
if (addedYears.includes(year)) {
|
||||
alert("This AY is already added");
|
||||
return;
|
||||
}
|
||||
|
||||
addedYears.push(year);
|
||||
|
||||
// Add header column
|
||||
const headerRow = document.getElementById("tableHeader");
|
||||
const th = document.createElement("th");
|
||||
th.innerText = "Utilized " + year;
|
||||
|
||||
// Insert before Balance column
|
||||
headerRow.insertBefore(th, headerRow.children[headerRow.children.length - 2]);
|
||||
|
||||
// Add input cells to all existing rows
|
||||
document.querySelectorAll("#matTable tbody tr").forEach(tr => {
|
||||
const td = document.createElement("td");
|
||||
td.innerHTML = `<input type="number">`;
|
||||
tr.insertBefore(td, tr.children[tr.children.length - 2]);
|
||||
});
|
||||
}
|
||||
|
||||
/* =====================================================
|
||||
ADD NEW ROW
|
||||
===================================================== */
|
||||
function addRow() {
|
||||
const tbody = document.querySelector("#matTable tbody");
|
||||
const tr = document.createElement("tr");
|
||||
|
||||
let utilizedCols = "";
|
||||
addedYears.forEach(() => {
|
||||
utilizedCols += `<td><input type="number"></td>`;
|
||||
});
|
||||
|
||||
tr.innerHTML = `
|
||||
<td contenteditable="true"></td>
|
||||
<td><input></td>
|
||||
${utilizedCols}
|
||||
<td><input></td>
|
||||
<td>
|
||||
<button onclick="saveRow(this)">Save</button>
|
||||
</td>
|
||||
`;
|
||||
|
||||
tbody.appendChild(tr);
|
||||
}
|
||||
|
||||
/* =====================================================
|
||||
SAVE SINGLE ROW
|
||||
===================================================== */
|
||||
function saveRow(btn) {
|
||||
const tr = btn.closest("tr");
|
||||
const inputs = tr.querySelectorAll("input");
|
||||
|
||||
const financialYear = tr.children[0].innerText.trim();
|
||||
|
||||
if (!financialYear) {
|
||||
alert("AY is required");
|
||||
return;
|
||||
}
|
||||
|
||||
let payload = {
|
||||
financial_year: financialYear,
|
||||
mat_credit: inputs[0].value || 0,
|
||||
balance: inputs[inputs.length - 1].value || 0,
|
||||
utilization: []
|
||||
};
|
||||
|
||||
addedYears.forEach((year, i) => {
|
||||
const value = inputs[i + 1].value;
|
||||
if (value) {
|
||||
payload.utilization.push({
|
||||
year: year,
|
||||
amount: value
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
fetch("/save_mat_row", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
.then(res => res.json().then(j => ({ ok: res.ok, data: j })))
|
||||
.then(res => {
|
||||
if (res.ok) {
|
||||
alert("✅ " + res.data.message);
|
||||
} else {
|
||||
alert("⚠️ " + res.data.error);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
alert("Server error");
|
||||
});
|
||||
}
|
||||
|
||||
/* =====================================================
|
||||
SAVE ALL ROWS
|
||||
===================================================== */
|
||||
function saveAll() {
|
||||
let rows = [];
|
||||
|
||||
document.querySelectorAll("#matTable tbody tr").forEach(tr => {
|
||||
const inputs = tr.querySelectorAll("input");
|
||||
const financialYear = tr.children[0].innerText.trim();
|
||||
|
||||
if (!financialYear) return;
|
||||
|
||||
let row = {
|
||||
financial_year: financialYear,
|
||||
mat_credit: inputs[0].value || 0,
|
||||
balance: inputs[inputs.length - 1].value || 0,
|
||||
utilization: []
|
||||
};
|
||||
|
||||
addedYears.forEach((year, i) => {
|
||||
const value = inputs[i + 1].value;
|
||||
if (value) {
|
||||
row.utilization.push({
|
||||
year: year,
|
||||
amount: value
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
rows.push(row);
|
||||
});
|
||||
|
||||
if (!rows.length) {
|
||||
alert("No data to save");
|
||||
return;
|
||||
}
|
||||
|
||||
fetch("/save_mat_all", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(rows)
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
let msg = "✅ " + res.message;
|
||||
if (res.skipped && res.skipped.length) {
|
||||
msg += "\n\nSkipped AY:\n" + res.skipped.join(", ");
|
||||
}
|
||||
alert(msg);
|
||||
})
|
||||
.catch(() => {
|
||||
alert("Server error");
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user