6 Commits

8 changed files with 541 additions and 476 deletions

View File

@@ -1,5 +1,5 @@
import config
from flask import Blueprint, render_template, request, redirect, url_for, jsonify
from flask import Blueprint, render_template, request, redirect, url_for, jsonify, flash
from flask_login import login_required
from model.State import State
@@ -74,8 +74,15 @@ def edit_block(block_id):
if request.method == 'POST':
block.EditBlock(request, block_id)
return block.resultMessage
block.resultMessage
if block.resultMessage:
flash("Block updated successfully!", "success")
return redirect(url_for('block.add_block'))
else:
flash(block.resultMessage, "error")
connection = config.get_db_connection()
cursor = connection.cursor()

View File

@@ -83,17 +83,29 @@ def check_village():
return village.CheckVillage(request=request)
# ------------------------- Delete Village -------------------------
@village_bp.route('/delete_village/<int:village_id>')
@login_required
def delete_village(village_id):
village = Village()
village.DeleteVillage(request=request, village_id=village_id)
flash(village.resultMessage, "success" if village.isSuccess else "error")
return redirect(url_for('village.add_village'))
# ✅ Convert resultMessage to string if it's a Response or tuple
raw_msg = village.resultMessage
if isinstance(raw_msg, tuple):
# e.g., (<Response ...>, 200)
msg = "Village deleted successfully!"
elif hasattr(raw_msg, 'get_data'):
# Flask Response object
msg = raw_msg.get_data(as_text=True) # get raw text
else:
# fallback
msg = str(raw_msg) if raw_msg else "Village deleted successfully!"
return jsonify({
"status": "success" if village.isSuccess else "error",
"message": msg
})
# ------------------------- Edit Village -------------------------
@village_bp.route('/edit_village/<int:village_id>', methods=['GET', 'POST'])

View File

@@ -1,4 +1,4 @@
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from flask import Blueprint, render_template, request, redirect, url_for, jsonify
from model.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType
from model.Log import LogData, LogHelper
@@ -147,7 +147,7 @@ class Block:
self.isSuccess = block.isSuccess
self.resultMessage = block.resultMessage
return render_template('add_block.html')
return
# ----------------------------------------------------------
# Delete Block

View File

@@ -1,6 +1,3 @@
from flask_login import current_user
from model.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType
from model.Log import LogHelper
@@ -38,7 +35,6 @@ class ItemCRUD:
def __init__(self, itemType):
self.isSuccess = False
self.resultMessage = ""
self.response = {} # ✅ ADDED
self.itemCRUDType = itemType
self.itemCRUDMapping = itemCRUDMapping(itemType)
@@ -60,14 +56,16 @@ class ItemCRUD:
connection.commit()
self.isSuccess = True
self.response = ResponseHandler.delete_success(self.itemCRUDMapping.name)
self.resultMessage = self.response["message"]
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.delete_success(self.itemCRUDMapping.name), 200
)
except mysql.connector.Error as e:
print(f"Error deleting {self.itemCRUDMapping.name}: {e}")
self.isSuccess = False
self.response = ResponseHandler.delete_failure(self.itemCRUDMapping.name)
self.resultMessage = self.response["message"]
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.delete_failure(self.itemCRUDMapping.name), 500
)
finally:
cursor.close()
@@ -81,8 +79,9 @@ class ItemCRUD:
connection = config.get_db_connection()
if not connection:
self.isSuccess = False
self.response = ResponseHandler.add_failure(self.itemCRUDMapping.name)
self.resultMessage = self.response["message"]
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.db_connection_failure(), 500
)
return
cursor = connection.cursor()
@@ -93,8 +92,12 @@ class ItemCRUD:
)
try:
# SUBCONTRACTOR
# ======================================================
# SUBCONTRACTOR (MULTI-FIELD)
# ======================================================
if data:
# Duplicate check
cursor.callproc(storedprocfetch, (data['Contractor_Name'],))
existing_item = None
@@ -103,10 +106,12 @@ class ItemCRUD:
if existing_item:
self.isSuccess = False
self.response = ResponseHandler.already_exists(self.itemCRUDMapping.name)
self.resultMessage = self.response["message"]
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.already_exists(self.itemCRUDMapping.name), 409
)
return
# Insert
cursor.callproc(storedprocadd, (
data['Contractor_Name'],
data['Address'],
@@ -122,17 +127,22 @@ class ItemCRUD:
connection.commit()
self.isSuccess = True
self.response = ResponseHandler.add_success(self.itemCRUDMapping.name)
self.resultMessage = self.response["message"]
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.add_success(self.itemCRUDMapping.name), 200
)
return
# NORMAL
# ======================================================
# NORMAL (Village / Block / State)
# ======================================================
if not re.match(RegEx.patternAlphabetOnly, childname):
self.isSuccess = False
self.response = ResponseHandler.invalid_name(self.itemCRUDMapping.name)
self.resultMessage = self.response["message"]
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400
)
return
# Duplicate check
if parentid is None:
cursor.callproc(storedprocfetch, (childname,))
else:
@@ -144,10 +154,12 @@ class ItemCRUD:
if existing_item:
self.isSuccess = False
self.response = ResponseHandler.already_exists(self.itemCRUDMapping.name)
self.resultMessage = self.response["message"]
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.already_exists(self.itemCRUDMapping.name), 409
)
return
# Insert
if parentid is None:
cursor.callproc(storedprocadd, (childname,))
else:
@@ -156,14 +168,17 @@ class ItemCRUD:
connection.commit()
self.isSuccess = True
self.response = ResponseHandler.add_success(self.itemCRUDMapping.name)
self.resultMessage = self.response["message"]
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.add_success(self.itemCRUDMapping.name), 200
)
except mysql.connector.Error as e:
print(f"Database Error: {e}")
self.isSuccess = False
self.response = ResponseHandler.add_failure(self.itemCRUDMapping.name)
self.resultMessage = self.response["message"]
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.add_failure(self.itemCRUDMapping.name), 500
)
finally:
cursor.close()
@@ -183,6 +198,9 @@ class ItemCRUD:
)
try:
# ======================================================
# SUBCONTRACTOR (MULTI-FIELD)
# ======================================================
if data:
cursor.callproc(storedprocupdate, (
childid,
@@ -200,14 +218,17 @@ class ItemCRUD:
connection.commit()
self.isSuccess = True
self.response = ResponseHandler.update_success(self.itemCRUDMapping.name)
self.resultMessage = self.response["message"]
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.update_success(self.itemCRUDMapping.name), 200
)
return
# ======================================================
# NORMAL
# ======================================================
if not re.match(RegEx.patternAlphabetOnly, childname):
self.isSuccess = False
self.response = ResponseHandler.update_failure(self.itemCRUDMapping.name)
self.resultMessage = self.response["message"]
self.resultMessage = ResponseHandler.update_failure(self.itemCRUDMapping.name)['message']
return
if parentid is None:
@@ -218,14 +239,14 @@ class ItemCRUD:
connection.commit()
self.isSuccess = True
self.response = ResponseHandler.update_success(self.itemCRUDMapping.name)
self.resultMessage = self.response["message"]
self.resultMessage = ResponseHandler.update_success(self.itemCRUDMapping.name)['message']
except mysql.connector.Error as e:
print(f"Error updating {self.itemCRUDMapping.name}: {e}")
self.isSuccess = False
self.response = ResponseHandler.update_failure(self.itemCRUDMapping.name)
self.resultMessage = self.response["message"]
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.update_failure(self.itemCRUDMapping.name), 500
)
finally:
cursor.close()
@@ -255,8 +276,9 @@ class ItemCRUD:
except mysql.connector.Error as e:
print(f"Error fetching {self.itemCRUDMapping.name}: {e}")
self.isSuccess = False
self.response = ResponseHandler.fetch_failure(self.itemCRUDMapping.name)
self.resultMessage = self.response["message"]
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500
)
return []
finally:
@@ -290,7 +312,7 @@ class ItemCRUD:
return data
# ----------------------------------------------------------
# CHECK ITEM (KEEP AS IS - API USE)
# CHECK ITEM
# ----------------------------------------------------------
def CheckItem(self, request, parentid, childname, storedprocfetch):

View File

@@ -1,264 +1,250 @@
window.onload = function () {
document.getElementById('Village_Name').focus();
if (document.getElementById('Village_Name')) {
document.getElementById('Village_Name').focus();
}
};
$(document).ready(function () {
// 🔥 RESTORE VIEW MODE AFTER RELOAD
var viewMode = localStorage.getItem("viewMode");
// RUN ONLY IF THIS PAGE HAS FORM/TABLE
if ($('#addForm').length && $('#addTable').length) {
if (viewMode === "table") {
$('#addForm').hide();
$('#addTable').show();
} else {
// ✅ DEFAULT VIEW → SHOW FORM
$('#addForm').show();
$('#addTable').hide();
// 🔥 BUTTON TOGGLE
$('#addButton').click(function () {
$('#addForm').show();
$('#addTable').hide();
});
$('#displayButton').click(function () {
$('#addForm').hide();
$('#addTable').show();
});
}
// 🔥 BUTTON TOGGLE LOGIC
$('#addButton').click(function () {
$('#addForm').show();
$('#addTable').hide();
localStorage.setItem("viewMode", "form");
});
$('#displayButton').click(function () {
$('#addForm').hide();
$('#addTable').show();
localStorage.setItem("viewMode", "table");
});
// ===============================
// STATE → DISTRICT
$('#state_Id').change(function () {
// ===============================
if ($('#state_Id').length) {
var stateId = $(this).val();
$('#state_Id').change(function () {
if (stateId) {
var stateId = $(this).val();
$.ajax({
url: '/get_districts/' + stateId,
type: 'GET',
if (stateId) {
success: function (data) {
$.ajax({
url: '/get_districts/' + stateId,
type: 'GET',
var districtDropdown = $('#district_Id');
success: function (data) {
districtDropdown.empty();
districtDropdown.append('<option value="" disabled selected>Select District</option>');
var districtDropdown = $('#district_Id');
data.forEach(function (district) {
districtDropdown.empty();
districtDropdown.append('<option value="" disabled selected>Select District</option>');
districtDropdown.append(
'<option value="' + district.id + '">' + district.name + '</option>'
);
data.forEach(function (district) {
districtDropdown.append(
'<option value="' + district.id + '">' + district.name + '</option>'
);
});
});
districtDropdown.prop('disabled', false);
}
});
}
});
districtDropdown.prop('disabled', false);
}
});
}
});
}
// ===============================
// DISTRICT → BLOCK
$('#district_Id').change(function () {
// ===============================
if ($('#district_Id').length) {
var districtId = $(this).val();
$('#district_Id').change(function () {
if (districtId) {
var districtId = $(this).val();
$.ajax({
url: '/get_blocks/' + districtId,
type: 'GET',
if (districtId) {
success: function (data) {
$.ajax({
url: '/get_blocks/' + districtId,
type: 'GET',
var blockDropdown = $('#block_Id');
success: function (data) {
blockDropdown.empty();
blockDropdown.append('<option value="" disabled selected>Select Block</option>');
var blockDropdown = $('#block_Id');
data.forEach(function (block) {
blockDropdown.empty();
blockDropdown.append('<option value="" disabled selected>Select Block</option>');
blockDropdown.append(
'<option value="' + block.id + '">' + block.name + '</option>'
);
data.forEach(function (block) {
blockDropdown.append(
'<option value="' + block.id + '">' + block.name + '</option>'
);
});
});
blockDropdown.prop('disabled', false);
}
});
}
});
blockDropdown.prop('disabled', false);
}
});
}
});
}
// ===============================
// VILLAGE NAME VALIDATION
$('#Village_Name').on('input', function () {
// ===============================
if ($('#Village_Name').length) {
var villageName = $(this).val();
var validPattern = /^[A-Za-z ]*$/;
$('#Village_Name').on('input', function () {
if (!validPattern.test(villageName)) {
var villageName = $(this).val();
var validPattern = /^[A-Za-z ]*$/;
$('#villageMessage')
.text('Only letters and spaces are allowed!')
.css('color', 'red');
if (!validPattern.test(villageName)) {
$('#submitVillage').prop('disabled', true);
$('#villageMessage')
.text('Only letters and spaces are allowed!')
.css('color', 'red');
} else {
$('#submitVillage').prop('disabled', true);
$('#villageMessage').text('');
$('#submitVillage').prop('disabled', false);
} else {
}
});
$('#villageMessage').text('');
$('#submitVillage').prop('disabled', false);
}
});
}
// ===============================
// CHECK DUPLICATE VILLAGE
$('#Village_Name, #block_Id').on('change keyup', function () {
// ===============================
if ($('#Village_Name').length && $('#block_Id').length) {
var blockId = $('#block_Id').val();
var villageName = $('#Village_Name').val().trim();
$('#Village_Name, #block_Id').on('change keyup', function () {
if (blockId && villageName) {
var blockId = $('#block_Id').val();
var villageName = $('#Village_Name').val().trim();
$.ajax({
if (blockId && villageName) {
url: '/check_village',
type: 'POST',
$.ajax({
url: '/check_village',
type: 'POST',
data: {
block_Id: blockId,
Village_Name: villageName
},
data: {
block_Id: blockId,
Village_Name: villageName
},
success: function (response) {
success: function (response) {
if (response.status === 'exists') {
if (response.status === 'exists') {
$('#villageMessage')
.text(response.message)
.css('color', 'red');
$('#submitVillage').prop('disabled', true);
} else {
$('#villageMessage')
.text(response.message)
.css('color', 'green');
$('#submitVillage').prop('disabled', false);
}
},
error: function () {
$('#villageMessage')
.text(response.message)
.text('Error checking village name')
.css('color', 'red');
$('#submitVillage').prop('disabled', true);
}
});
}
});
}
// ===============================
// ADD VILLAGE (SAFE SCOPE FIX)
// ===============================
if ($('#villageForm').length) {
$('#villageForm').submit(function (event) {
event.preventDefault();
$.ajax({
url: '/add_village',
type: 'POST',
data: $(this).serialize(),
success: function (response) {
if (response && response.status === 'success') {
alert(response.message || 'Village added successfully!');
// ✅ clear form
$('#villageForm')[0].reset();
// ✅ switch to table
$('#addForm').hide();
$('#addTable').show();
// optional refresh
location.reload();
} else {
$('#villageMessage')
.text(response.message)
.css('color', 'green');
$('#submitVillage').prop('disabled', false);
alert(response.message || 'Error adding village. Please try again.');
}
},
error: function () {
$('#villageMessage')
.text('Error checking village name')
.css('color', 'red');
$('#submitVillage').prop('disabled', true);
alert('An error occurred. Please try again.');
}
});
}
});
// ADD VILLAGE
$('#villageForm').submit(function (event) {
event.preventDefault();
$.ajax({
url: '/add_village',
type: 'POST',
data: $(this).serialize(),
success: function (response) {
if (response.status === 'success') {
alert('Village added successfully!');
location.reload();
} else {
alert(response.message || 'Error adding village. Please try again.');
}
},
error: function () {
alert('An error occurred. Please try again.');
}
});
});
}
});
// 🔥 DELETE FUNCTION (UPDATED)
function deleteVillage(villageId) {
if (!confirm("Are you sure you want to delete this village?")) {
return;
}
// ✅ save that user is on table
localStorage.setItem("viewMode", "table");
// ===============================
// DELETE FUNCTION (SAFE)
// ===============================
function deleteVillage(villageId, element) {
if (!confirm("Are you sure you want to delete this village?")) return;
$.ajax({
url: '/delete_village/' + villageId,
type: 'GET',
success: function () {
setTimeout(function () {
alert("Village deleted successfully!");
// reload but stay on table
location.reload();
}, 1000);
dataType: 'json',
success: function (response) {
alert(response.message); // ✅ now shows "Village deleted successfully!"
if (element) $(element).closest("tr").remove();
},
error: function () {
alert("Error deleting village. Please try again.");
}
});
}

View File

@@ -1,5 +1,6 @@
{% extends 'base.html' %}
{% block content %}
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -8,91 +9,106 @@
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
<script src="{{ url_for('static', filename='js/invoice.js') }}"></script>
</head>
<body>
<div class="button-container">
<button id="addButton" class="action-button">Add</button>
<button id="displayButton" class="action-button">Display</button>
</div>
<div id="addForm" style="display: none;">
<h2>Add Payment</h2>
<form action="/add_payment" method="POST" onsubmit="showSuccessAlert(event)">
<div class="row1">
<div>
<label for="subcontractor">Subcontractor Name:</label>
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/>
<input type="hidden" id="subcontractor_id" name="subcontractor_id"/>
<div id="subcontractor_list" class="autocomplete-items"></div>
</div>
</div>
<label for="PMC_No">PMC No:</label><br>
<select id="PMC_No" name="PMC_No" required>
<option value="">Select PMC No</option>
</select><br><br>
<label for="invoice_No">Invoice No:</label><br>
<input type="number" step="0.01" id="invoice_No" name="invoice_No" ><br><br>
<label for="Payment_Amount">Amount:</label><br>
<input type="number" step="0.01" id="Payment_Amount" name="Payment_Amount" required oninput="calculateTDSAndTotal()"><br><br>
<label for="TDS_Percentage">TDS Percentage (%):</label><br>
<input type="number" step="0.01" id="TDS_Percentage" name="TDS_Percentage" oninput="calculateTDSAndTotal()"><br><br>
<label for="TDS_Payment_Amount">TDS Amount:</label><br>
<input type="number" step="0.01" id="TDS_Payment_Amount" name="TDS_Payment_Amount" required readonly><br><br>
<label for="total_amount">Total Amount:</label><br>
<input type="number" step="0.01" id="total_amount" name="total_amount" required readonly><br><br>
<label for="utr">UTR:</label><br>
<input type="text" id="utr" name="utr"><br><br>
<button type="submit">Submit Payment</button>
</form>
</div>
<div id="successPopup" class="success-popup">
<i>&#10004;</i> Payment added successfully!
</div>
<div id="addTable" style="display: none;">
<div class="search-container">
<h2>Payment History</h2>
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
<div class="button-container">
<button id="addButton" class="action-button">Add</button>
<button id="displayButton" class="action-button">Display</button>
</div>
<table id="sortableTable" border="1">
<thead>
<tr>
<th class="sortable-header">Payment ID</th>
<th class="sortable-header">PMC No</th>
<th>Invoice No</th>
<th>Payment Amount</th>
<th>TDS Amount</th>
<th>Total Amount</th>
<th>UTR</th>
<th>Update</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{% for payment in payments %}
<tr>
<td>{{ payment[0] }}</td>
<td>{{ payment[1] }}</td>
<td>{{ payment[2] }}</td>
<td>{{ payment[3] }}</td>
<td>{{ payment[4] }}</td>
<td>{{ payment[5] }}</td>
<td>{{ payment[6] }}</td>
<td><a href="/edit_payment/{{ payment[0] }}"><img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" class="icon"></a></td>
<td><a href="/delete_payment/{{ payment[0] }}" onclick="return confirm('Are you sure you want to delete this payment?')"><img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" class="icon"></a></td>
</tr>
<!-- <tr>
<div id="addForm" style="display: none;">
<h2>Add Payment</h2>
<form action="/add_payment" method="POST" onsubmit="showSuccessAlert(event)">
<div class="row1">
<div>
<label for="subcontractor">Subcontractor Name:</label>
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off" />
<input type="hidden" id="subcontractor_id" name="subcontractor_id" />
<div id="subcontractor_list" class="autocomplete-items"></div>
</div>
</div>
<label for="PMC_No">PMC No:</label><br>
<select id="PMC_No" name="PMC_No" required>
<option value="">Select PMC No</option>
</select><br><br>
<label for="invoice_No">Invoice No:</label><br>
<input type="number" step="0.01" id="invoice_No" name="invoice_No"><br><br>
<label for="Payment_Amount">Amount:</label><br>
<input type="number" step="0.01" id="Payment_Amount" name="Payment_Amount" required
oninput="calculateTDSAndTotal()"><br><br>
<label for="TDS_Percentage">TDS Percentage (%):</label><br>
<input type="number" step="0.01" id="TDS_Percentage" name="TDS_Percentage"
oninput="calculateTDSAndTotal()"><br><br>
<label for="TDS_Payment_Amount">TDS Amount:</label><br>
<input type="number" step="0.01" id="TDS_Payment_Amount" name="TDS_Payment_Amount" required
readonly><br><br>
<label for="total_amount">Total Amount:</label><br>
<input type="number" step="0.01" id="total_amount" name="total_amount" required readonly><br><br>
<label for="utr">UTR:</label><br>
<input type="text" id="utr" name="utr"><br><br>
<button type="submit">Submit Payment</button>
</form>
</div>
<div id="successPopup" class="success-popup">
<i>&#10004;</i> Payment added successfully!
</div>
<div id="addTable" style="display: none;">
<div class="search-container">
<h2>Payment History</h2>
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
</div>
<table id="sortableTable" border="1">
<thead>
<tr>
<th class="sortable-header">Payment ID</th>
<th class="sortable-header">PMC No</th>
<th>Invoice No</th>
<th>Payment Amount</th>
<th>TDS Amount</th>
<th>Total Amount</th>
<th>UTR</th>
<th>Update</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{% for payment in payments %}
<tr>
<td>{{ payment[0] }}</td>
<td>{{ payment[1] }}</td>
<td>{{ payment[2] }}</td>
<td>{{ payment[3] }}</td>
<td>{{ payment[4] }}</td>
<td>{{ payment[5] }}</td>
<td>{{ payment[6] }}</td>
<td><a href="/edit_payment/{{ payment[0] }}"><img
src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit"
class="icon"></a></td>
<td>
<form action="{{ url_for('payment_bp.delete_payment', payment_id=payment[0]) }}" method="POST"
style="display:inline;">
<button type="submit"
onclick="return confirm('Are you sure you want to delete this payment?')">
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}"
alt="Delete" class="icon">
</button>
</form>
</td>
</tr>
<!-- <tr>
<td>{{ payment['Payment_Id'] }}</td>
<td>{{ payment['PMC_No'] }}</td>
<td>{{ payment['invoice_no'] }}</td>
@@ -103,91 +119,91 @@
<td><a href="/edit_payment/{{ payment['Payment_Id'] }}"><img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" class="icon"></a></td>
<td><a href="/delete_payment/{{ payment['Payment_Id'] }}" onclick="return confirm('Are you sure you want to delete this payment?')"><img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" class="icon"></a></td>
</tr> -->
{% endfor %}
</tbody>
</table>
</div>
{% endfor %}
</tbody>
</table>
</div>
<script>
document.getElementById("subcontractor").addEventListener("input", function () {
const query = this.value;
const list = document.getElementById("subcontractor_list");
<script>
document.getElementById("subcontractor").addEventListener("input", function () {
const query = this.value;
const list = document.getElementById("subcontractor_list");
if (query.length < 2) {
list.innerHTML = '';
return;
}
if (query.length < 2) {
list.innerHTML = '';
return;
}
fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`)
.then(response => response.json())
.then(data => {
list.innerHTML = '';
data.results.forEach(item => {
const div = document.createElement("div");
div.setAttribute("data-id", item.id);
div.textContent = item.name;
list.appendChild(div);
});
});
});
document.getElementById("subcontractor_list").addEventListener("click", function (e) {
const selectedId = e.target.getAttribute("data-id");
const selectedName = e.target.textContent;
if (selectedId) {
document.getElementById("subcontractor_id").value = selectedId;
document.getElementById("subcontractor").value = selectedName;
document.getElementById("subcontractor_list").innerHTML = ""; // hide the list
console.log("Contractor id is", selectedId);
// Fetch PMC numbers
fetch(`/get_pmc_nos_by_subcontractor/${encodeURIComponent(selectedId)}`)
.then(response => response.json())
.then(data => {
console.log("Fetched PMC Nos:", data.pmc_nos);
const pmcDropdown = document.getElementById("PMC_No");
pmcDropdown.innerHTML = "";
const defaultOption = document.createElement("option");
defaultOption.value = "";
defaultOption.textContent = "Select PMC No";
pmcDropdown.appendChild(defaultOption);
data.pmc_nos.forEach(pmc => {
const option = document.createElement("option");
option.value = pmc;
option.textContent = pmc;
pmcDropdown.appendChild(option);
fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`)
.then(response => response.json())
.then(data => {
list.innerHTML = '';
data.results.forEach(item => {
const div = document.createElement("div");
div.setAttribute("data-id", item.id);
div.textContent = item.name;
list.appendChild(div);
});
});
});
if (data.pmc_nos.length === 0) {
alert("No PMC Nos found for this subcontractor.");
}
})
.catch(error => {
console.error("Error fetching PMC Nos:", error);
alert("Failed to fetch PMC numbers.");
});
}
});
</script>
document.getElementById("subcontractor_list").addEventListener("click", function (e) {
const selectedId = e.target.getAttribute("data-id");
const selectedName = e.target.textContent;
<script>
function calculateTDSAndTotal() {
const amount = parseFloat(document.getElementById("Payment_Amount").value) || 0;
const tdsPercent = parseFloat(document.getElementById("TDS_Percentage").value) || 0;
if (selectedId) {
document.getElementById("subcontractor_id").value = selectedId;
document.getElementById("subcontractor").value = selectedName;
document.getElementById("subcontractor_list").innerHTML = ""; // hide the list
const tdsAmount = (amount * tdsPercent / 100).toFixed(2);
const totalAmount = (amount - tdsAmount).toFixed(2);
console.log("Contractor id is", selectedId);
document.getElementById("TDS_Payment_Amount").value = tdsAmount;
document.getElementById("total_amount").value = totalAmount;
}
</script>
// Fetch PMC numbers
fetch(`/get_pmc_nos_by_subcontractor/${encodeURIComponent(selectedId)}`)
.then(response => response.json())
.then(data => {
console.log("Fetched PMC Nos:", data.pmc_nos);
const pmcDropdown = document.getElementById("PMC_No");
pmcDropdown.innerHTML = "";
const defaultOption = document.createElement("option");
defaultOption.value = "";
defaultOption.textContent = "Select PMC No";
pmcDropdown.appendChild(defaultOption);
data.pmc_nos.forEach(pmc => {
const option = document.createElement("option");
option.value = pmc;
option.textContent = pmc;
pmcDropdown.appendChild(option);
});
if (data.pmc_nos.length === 0) {
alert("No PMC Nos found for this subcontractor.");
}
})
.catch(error => {
console.error("Error fetching PMC Nos:", error);
alert("Failed to fetch PMC numbers.");
});
}
});
</script>
<script>
function calculateTDSAndTotal() {
const amount = parseFloat(document.getElementById("Payment_Amount").value) || 0;
const tdsPercent = parseFloat(document.getElementById("TDS_Percentage").value) || 0;
const tdsAmount = (amount * tdsPercent / 100).toFixed(2);
const totalAmount = (amount - tdsAmount).toFixed(2);
document.getElementById("TDS_Payment_Amount").value = tdsAmount;
document.getElementById("total_amount").value = totalAmount;
}
</script>
<script src="{{ url_for('static', filename='js/showSuccessAlert.js') }}"></script>
<script src="{{ url_for('static', filename='js/showSuccessAlert.js') }}"></script>
</body>
{% endblock %}
{% endblock %}

View File

@@ -1,99 +1,129 @@
{% extends 'base.html' %}
{% block content %}
{% extends 'base.html' %} {% block content %}
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Village Management</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
<script src="{{ url_for('static', filename='js/village.js') }}"></script>
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Village Management</title>
<link
rel="stylesheet"
type="text/css"
href="{{ url_for('static', filename='css/style.css') }}"
/>
<script src="{{ url_for('static', filename='js/village.js') }}"></script>
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
</head>
<body>
<!-- Button Container to Center Buttons -->
<div class="button-container">
<!-- Button Container to Center Buttons -->
<div class="button-container">
<button id="addButton" class="action-button">Add</button>
<button id="displayButton" class="action-button">Display</button>
</div>
</div>
<div id="addForm" style="display: none;">
<div id="addForm" style="display: none">
<div class="container">
<div class="form-block">
<h2>Add a New Village</h2>
<form id="villageForm" method="POST">
<label for="state_Id">State:</label>
<select id="state_Id" name="state_Id" required>
<option value="" disabled selected>Select State</option>
{% for state in states %}
<option value="{{ state[0] }}">{{ state[1] }}</option>
{% endfor %}
</select>
<label for="district_Id">District:</label>
<select id="district_Id" name="district_Id" required disabled>
<option value="" disabled selected>Select District</option>
</select>
<div class="form-block">
<h2>Add a New Village</h2>
<form id="villageForm" method="POST">
<label for="state_Id">State:</label>
<select id="state_Id" name="state_Id" required>
<option value="" disabled selected>Select State</option>
{% for state in states %}
<option value="{{ state[0] }}">{{ state[1] }}</option>
{% endfor %}
</select>
<label for="block_Id">Block:</label>
<select id="block_Id" name="block_Id" required disabled>
<option value="" disabled selected>Select Block</option>
</select>
<label for="district_Id">District:</label>
<select id="district_Id" name="district_Id" required disabled>
<option value="" disabled selected>Select District</option>
</select>
<label for="Village_Name">Village Name:</label>
<input type="text" id="Village_Name" name="Village_Name" placeholder="Enter Village Name" required>
<span id="villageMessage"></span>
<label for="block_Id">Block:</label>
<select id="block_Id" name="block_Id" required disabled>
<option value="" disabled selected>Select Block</option>
</select>
<button type="submit" id="submitVillage" disabled>Add Village</button>
</form>
</div>
<label for="Village_Name">Village Name:</label>
<input
type="text"
id="Village_Name"
name="Village_Name"
placeholder="Enter Village Name"
required
/>
<span id="villageMessage"></span>
<button type="submit" id="submitVillage" disabled>Add Village</button>
</form>
</div>
</div>
</div>
</div>
<div id="addTable" style="display: none;">
<div id="addTable" style="display: none">
<div class="search-container">
<h2>Display Villages</h2>
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
<h2>Display Villages</h2>
<input
type="text"
id="searchBar"
placeholder="Searching..."
onkeyup="searchTable()"
/>
</div>
<table id="sortableTable" border="1">
<tr>
<th>Village Sr No</th>
<th class="sortable-header">
Village Name
<span class="sort-buttons">
<span class="sort-asc">⬆️</span>
<span class="sort-desc">⬇️</span>
</span></th>
<th class="sortable-header">Block Name
<span class="sort-buttons">
<span class="sort-asc">⬆️</span>
<span class="sort-desc">⬇️</span>
</span></th>
<th>Update</th>
<th>Delete</th>
</tr>
{% for village in villages %}
<tr>
<td>{{ village[0] }}</td>
<td>{{ village[1] }}</td>
<td>{{ village[2] }}</td>
<td>
<a href="{{ url_for('village.edit_village', village_id=village[0]) }}">
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit"
class="icon">
</a>
</td>
<td>
<a href="#"
onclick="deleteVillage({{ village[0] }}); return false;">
<tr>
<th>Village Sr No</th>
<th class="sortable-header">
Village Name
<span class="sort-buttons">
<span class="sort-asc">⬆️</span>
<span class="sort-desc">⬇️</span>
</span>
</th>
<th class="sortable-header">
Block Name
<span class="sort-buttons">
<span class="sort-asc">⬆️</span>
<span class="sort-desc">⬇️</span>
</span>
</th>
<th>Update</th>
<th>Delete</th>
</tr>
{% for village in villages %}
<tr>
<td>{{ village[0] }}</td>
<td>{{ village[1] }}</td>
<td>{{ village[2] }}</td>
<td>
<a
href="{{ url_for('village.edit_village', village_id=village[0]) }}"
>
<img
src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}"
alt="Edit"
class="icon"
/>
</a>
</td>
<td>
<a href="javascript:void(0);"
onclick="deleteVillage({{ village[0] }}, this)">
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}"
alt="Delete" class="icon">
class="icon">
</a>
</td>
</tr>
{% endfor %}
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
<!-- Flash Alerts -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<script>
{% for category, message in messages %}
alert("{{ message }}");
{% endfor %}
</script>
{% endif %}
{% endwith %}
</body>
{% endblock %}

View File

@@ -28,25 +28,17 @@
<button type="submit">Update Village</button>
</form>
</div>
<!-- Flash Message (Hidden, used for JS popup) -->
<!-- Flash Messages (hidden, used for JS popup) -->
<div id="flash-messages-container" style="display:none;">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div id="flash-message" data-category="{{ category }}" style="display:none;">{{ message }}</div>
{% endfor %}
{% endif %}
{% if messages %}
{% for category, message in messages %}
<div class="flash-message" data-category="{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
</div>
<script>
window.onload = function () {
const flash = document.getElementById('flash-message');
if (flash && flash.innerText.trim() !== "") {
alert(flash.innerText);
}
};
</script>
</body>
{% endblock %}