Merge branch 'pankaj-dev'

This commit is contained in:
2026-08-06 12:46:16 +05:30
52 changed files with 4744 additions and 913 deletions

View File

@@ -0,0 +1,208 @@
{% extends "base.html" %}
{% block content %}
<div class="container-fluid py-4">
<!-- HEADER -->
<div class="card shadow-sm border-0 mb-4">
<div class="card-body">
<div class="d-flex flex-column flex-lg-row justify-content-between align-items-lg-center">
<div class="mb-3 mb-lg-0">
<h3 class="fw-bold text-primary mb-1"> <i class="bi bi-clock-history me-2"></i> Activity Logs </h3>
<small class="text-muted"> View application logs, search records and download log files. </small>
</div>
<div class="d-flex flex-wrap gap-2">
<a id="downloadLogBtn" href="{{ url_for('activity.download_log') }}?file={{ file_name }}" class="btn btn-success">
<i class="bi bi-download me-1"></i>
Download Log
</a>
<a href="{{ url_for('activity.activity') }}" class="btn btn-outline-primary">
<i class="bi bi-arrow-clockwise me-1"></i>
Refresh
</a>
</div>
</div>
</div>
</div>
<!-- FILTERS -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">
<i class="bi bi-funnel-fill me-2"></i> Filters
</h5>
</div>
<div class="card-body">
<form method="GET">
<div class="row g-3">
<!-- Log File select -->
<div class="col-xl-2 col-lg-4 col-md-6">
<label class="form-label fw-bold">Log File</label>
<select class="form-select" name="file">
<option value="app.log" {% if file_name=="app.log" %}selected{% endif %}> Application </option>
<option value="debug.log" {% if file_name=="debug.log" %}selected{% endif %}> Debug </option>
<option value="error.log" {% if file_name=="error.log" %}selected{% endif %}> Error </option>
</select>
</div>
<!-- User search -->
<div class="col-xl-2 col-lg-4 col-md-6">
<label class="form-label fw-bold">User</label>
<input type="text" class="form-control" name="user" value="{{ user }}" placeholder="Username">
</div>
<!-- Level -->
<div class="col-xl-2 col-lg-4 col-md-6">
<label class="form-label fw-bold">Level</label>
<select class="form-select" name="level">
<option value="">All</option>
<option value="INFO"
{% if level=="INFO" %}selected{% endif %}>
INFO
</option>
<option value="WARNING"
{% if level=="WARNING" %}selected{% endif %}>
WARNING
</option>
<option value="ERROR"
{% if level=="ERROR" %}selected{% endif %}>
ERROR
</option>
</select>
</div>
<!-- From Date -->
<div class="col-xl-2 col-lg-4 col-md-6">
<label class="form-label fw-bold">From Date</label>
<input type="date" class="form-control" name="from_date" value="{{ from_date }}">
</div>
<!-- To Date -->
<div class="col-xl-2 col-lg-4 col-md-6">
<label class="form-label fw-bold">To Date</label>
<input type="date" class="form-control" name="to_date" value="{{ to_date }}">
</div>
<!-- Search button -->
<div class="col-xl-2 col-lg-12">
<label class="form-label fw-bold">Search</label>
<input type="text" class="form-control" name="search" value="{{ search }}" placeholder="Search">
</div>
</div>
<hr>
<!-- Search and Reset button -->
<div class="d-flex gap-2">
<button class="btn btn-primary">
<i class="bi bi-search"></i> Search
</button>
<button type="reset" class="btn btn-secondary">
<i class="bi bi-arrow-clockwise"></i> Reset
</button>
</div>
</form>
</div>
</div>
<!-- LOG TABLE -->
<div class="card shadow-sm">
<!-- Log Entries -->
<div class="card-header bg-dark text-white">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="bi bi-list-ul me-2"></i>
Log Entries
</h5>
<span class="badge bg-light text-dark">
{{ logs|length }} Records
</span>
</div>
</div>
<!-- Show table Entries -->
<div class="card-body p-0">
<div class="table-responsive" style="max-height:600px; overflow-y:auto; font-size:12px;">
<table class="table table-striped table-hover table-bordered align-middle mb-0 small">
<thead class="table-dark sticky-top">
<tr>
<th>Sr No</th>
<th>Date & Time</th>
<th>User</th>
<th>Level</th>
<th>Module</th>
<th>Activity</th>
</tr>
</thead>
<tbody>
{% for log in logs %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ log.date }}</td>
<td>{{ log.user }}</td>
<td>
{% if log.level=="INFO" %}
<span class="badge bg-success">INFO</span>
{% elif log.level=="WARNING" %}
<span class="badge bg-warning text-dark">WARNING</span>
{% else %}
<span class="badge bg-danger">ERROR</span>
{% endif %}
</td>
<td>{{ log.module }}</td>
<td>{{ log.message }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
const fileSelect = document.querySelector('select[name="file"]');
const downloadBtn = document.getElementById("downloadLogBtn");
function updateDownloadLink() {
downloadBtn.href =
"{{ url_for('activity.download_log') }}?file=" +
encodeURIComponent(fileSelect.value);
}
updateDownloadLink();
fileSelect.addEventListener("change", updateDownloadLink);
});
</script>
{% endblock %}

View File

@@ -5,18 +5,23 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ title if title else "Comparison Software" }}</title>
<link rel="icon" type="image/png" href="{{ url_for('static', filename='images/lcepl.png') }}">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<!-- Bootstrap Icons -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" >
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css">
<link rel="stylesheet" href="https://cdn.datatables.net/buttons/2.4.1/css/buttons.dataTables.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-multiselect@1.1.2/dist/css/bootstrap-multiselect.min.css">
</head>
<body class="bg-light">
<!-- NAVBAR -->
<!-- <nav class="navbar navbar-expand-lg navbar-dark bg-dark shadow-sm"> -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark shadow-sm fixed-top">
<div class="container-fluid">
@@ -45,17 +50,17 @@
<!-- Subcontractor Model -->
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#">
<i class="bi bi-people-fill me-1"></i> Subcontractor Model
<i class="bi bi-people-fill me-1"></i> Subcontractor List
</a>
<ul class="dropdown-menu dropdown-menu-dark">
<li>
<a class="dropdown-item" href="/subcontractor/add">
<i class="bi bi-plus-circle me-2"></i> Add Subcontractor
<i class="bi bi-plus-circle me-2"></i> New Add
</a>
</li>
<li>
<a class="dropdown-item" href="/subcontractor/list">
<i class="bi bi-list-ul me-2"></i> Subcontractor List
<i class="bi bi-list-ul me-2"></i> List
</a>
</li>
</ul>
@@ -64,9 +69,15 @@
<!-- Subcontractor File System -->
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#">
<i class="bi bi-folder-fill me-1"></i>Subcontractor File System
<i class="bi bi-folder-fill me-1"></i>Subcontractor RA Bills
</a>
<ul class="dropdown-menu dropdown-menu-dark">
<li>
<a class="dropdown-item" href="/dashboard/subcontractor_dashboard">
<i class="bi bi-speedometer2 me-2"></i> Subcontractor Dashboard
</a>
</li>
<li>
<a class="dropdown-item" href="/file/import_Subcontractor">
<i class="bi bi-upload me-2"></i> Import File
@@ -74,13 +85,7 @@
</li>
<li>
<a class="dropdown-item" href="/file/Subcontractor_report">
<i class="bi bi-download me-2"></i> Show Reports
</a>
</li>
<li>
<a class="dropdown-item" href="/dashboard/subcontractor_dashboard">
<i class="bi bi-speedometer2 me-2"></i> Subcontractor Dashboard
<i class="bi bi-arrow-left-right me-2"></i> Show Reports
</a>
</li>
@@ -90,7 +95,7 @@
<!-- Client System -->
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#">
<i class="bi bi-building me-1"></i> Client File System
<i class="bi bi-building me-1"></i> Client RA Bills
</a>
<ul class="dropdown-menu dropdown-menu-dark">
<li>
@@ -110,69 +115,118 @@
<!-- Reports -->
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#">
<i class="bi bi-building me-1"></i> Reports
<i class="bi bi-building me-1"></i> Comparison Report
</a>
<ul class="dropdown-menu dropdown-menu-dark">
<li>
<a class="dropdown-item" href="/report/comparison_report">
<i class="bi bi-arrow-left-right me-2"></i> client vs sub-cont. Comparison Report
<i class="bi bi-arrow-left-right me-2"></i> Client vs Subcontractor
</a>
</li>
<!-- <li>
<a class="dropdown-item" href="/file/client_vs_subcont">
<i class="bi bi-arrow-left-right me-2"></i> Comparison Report
</ul>
</li>
<!-- Masters -->
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="/engi">
<i class="bi bi-gear me-2"></i> Masters
</a>
<ul class="dropdown-menu dropdown-menu-dark">
<!-- Client Standard Rates -->
<li class="nav-item">
<a class="nav-link" href="/engi/subcontractor-rate">
<i class="bi bi-file-earmark-text me-1"></i> Rate Master
</a>
</li> -->
</li>
<!-- Client Standard Rates -->
<li class="nav-item">
<a class="nav-link" href="/engi/client-rate">
<i class="bi bi-file-earmark-text me-1"></i> Client Standard Rates
</a>
</li>
<!-- Formats -->
<li class="nav-item">
<a class="nav-link" href="/file_format">
<i class="bi bi-file-earmark-text me-1"></i> Formats
</a>
</li>
</ul>
</li>
<!-- Formats -->
<li class="nav-item">
<a class="nav-link" href="/file_format">
<i class="bi bi-file-earmark-text me-1"></i> Formats
</a>
</li>
<!-- USER DROPDOWN -->
<li class="nav-item dropdown">
<!-- USER DROPDOWN -->
{% if session.get("user_id") %}
<li class="nav-item dropdown ms-lg-3">
<a class="nav-link dropdown-toggle d-flex align-items-center text-white"
href="#" id="profileDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<a class="nav-link dropdown-toggle d-flex align-items-center gap-2" href="#"
data-bs-toggle="dropdown">
<i class="bi bi-person-circle fs-5"></i>
<span class="d-none d-lg-inline">
<i class="bi bi-person-circle fs-4"></i>
<span class="ms-2 fw-semibold">
{{ session.get("user_name") }}
</span>
</a>
<ul class="dropdown-menu dropdown-menu-end dropdown-menu-dark shadow">
<ul class="dropdown-menu dropdown-menu-end dropdown-menu-dark border-0 shadow-lg bg-dark p-0"
style="width:320px;">
<!-- User card -->
<li class="px-3 py-3 text-center border-bottom">
<i class="bi bi-person-circle fs-1"></i>
<div class="fw-semibold mt-1">
<!-- Profile Header -->
<li class="text-center py-4 border-bottom border-secondary">
<i class="bi bi-person-circle text-light" style="font-size:60px;"></i>
<h5 class="mt-2 mb-0 fw-bold">
{{ session.get("user_name") }}
</div>
<small class="text-muted">Logged in user</small>
</h5>
<small class="text-secondary">
{{ session.get("email") }}
</small>
</li>
<!-- Dashboard -->
<li>
<a class="dropdown-item" href="/dashboard">
<a class="dropdown-item text-light py-2" href="{{ url_for('dashboard.dashboard') }}">
<i class="bi bi-speedometer2 me-2"></i> Dashboard
</a>
</li>
<!-- Activity Log page -->
<li>
<a class="dropdown-item text-warning" href="/logout">
<i class="bi bi-box-arrow-right me-2"></i> Logout
<a class="dropdown-item text-light py-2" href="{{ url_for('activity.activity') }}">
<i class="bi bi-clock-history me-2"></i> Activity Log
</a>
</li>
<!-- Manage Account -->
<li>
<a class="dropdown-item py-2" href="#">
<i class="bi bi-gear me-2"></i> Manage Account
</a>
</li>
<li><hr class="dropdown-divider border-secondary m-0"></li>
<!-- Logout Account -->
<li>
<a class="dropdown-item text-warning py-2" href="{{ url_for('auth.logout') }}">
<i class="bi bi-box-arrow-right me-2"></i>
Logout
</a>
</li>
<!-- Footer -->
<li class="text-center py-3 bg-secondary bg-opacity-10 border-top border-secondary">
<small class="text-light">
<i class="bi bi-shield-check text-success"></i>
Secured by <strong>LCEPL</strong>
</small>
</li>
</ul>
</li>
{% endif %}
</ul>
</div>
@@ -183,19 +237,63 @@
<!-- PAGE CONTENT -->
<div class="container-fluid vh-100 pt-5 overflow-hidden">
<!-- FLASH MESSAGES -->
<div class="container mt-3">
<div class="position-fixed top-0 end-0 p-2 p-md-3 mt-5" style="z-index:1080; width:min(95vw,500px);">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }} alert-dismissible fade show">
{{ message }}
<button class="btn-close" data-bs-dismiss="alert"></button>
<div class="alert alert-{{ category }} alert-dismissible fade show shadow notification-alert mb-2"
role="alert">
<div class="d-flex align-items-start flex-wrap">
{% if category == 'success' %}
<i class="bi bi-check-circle-fill me-2 fs-5 flex-shrink-0"></i>
{% elif category == 'danger' %}
<i class="bi bi-x-circle-fill me-2 fs-5 flex-shrink-0"></i>
{% elif category == 'warning' %}
<i class="bi bi-exclamation-triangle-fill me-2 fs-5 flex-shrink-0"></i>
{% elif category == 'info' %}
<i class="bi bi-info-circle-fill me-2 fs-5 flex-shrink-0"></i>
{% endif %}
<div class="flex-grow-1">
<div class="fw-semibold">
{% if category == 'success' %}
Success
{% elif category == 'danger' %}
Error
{% elif category == 'warning' %}
Warning
{% elif category == 'info' %}
Information
{% endif %}
</div>
<div>
{{ message }}
</div>
<!-- Timeline -->
<div class="progress mt-2" style="height:5px;">
<div class="progress-bar progress-bar-striped progress-bar-animated timer-bar" role="progressbar" style="width:100%">
</div>
</div>
</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="alert">
</button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
</div>
</div>
<div class="overflow-auto h-100">
<div class="container mt-4">
@@ -204,10 +302,99 @@
</div>
</div>
<!-- Global Loading Overlay -->
<div id="globalLoader"
class="position-fixed top-0 start-0 w-100 h-100 bg-dark bg-opacity-50 d-none"
style="z-index:9999;">
<div class="d-flex justify-content-center align-items-center h-100">
<div class="card shadow-lg border-0 rounded-4" style="width:430px;">
<div class="card-body text-center p-5">
<div class="spinner-border text-primary" style="width:70px;height:70px;"role="status">
</div>
<h4 class="fw-bold mt-4 mb-2">Please Wait...</h4>
<p id="loaderMessage" class="text-muted mb-4">Processing your request...</p>
<div class="progress" style="height:8px;">
<div class="progress-bar progress-bar-striped progress-bar-animated"
style="width:100%">
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.1/js/dataTables.buttons.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.html5.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.print.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-multiselect@1.1.2/dist/js/bootstrap-multiselect.min.js"></script>
<script>
// show alert msg
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll(".notification-alert").forEach(function(alert){
const progressBar = alert.querySelector(".timer-bar");
let width = 100;
const interval = setInterval(function() {
width -= 2;
progressBar.style.width = width + "%";
if(width <= 0){
clearInterval(interval);
const bsAlert = bootstrap.Alert.getOrCreateInstance(alert);
bsAlert.close();
}
}, 100);
});
});
// show msg Processing your request
function showLoader(message = "Processing your request...") {
document.getElementById("loaderMessage").innerHTML = message;
document.getElementById("globalLoader").classList.remove("d-none");
}
// Automatically hide
function hideLoader(){
document.getElementById("globalLoader").classList.add("d-none");
}
// Automatically apply to all forms having class="loading-form"
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll(".loading-form").forEach(function(form){
form.addEventListener("submit", function(){
if(!this.checkValidity())
return;
showLoader();
const btn = this.querySelector("button[type='submit']");
if(btn){
btn.disabled = true;
btn.innerHTML = `
<span class="spinner-border spinner-border-sm me-2"></span>
Processing...
`;
}
});
});
});
</script>
</body>
</html>

View File

@@ -2,10 +2,10 @@
{% block content %}
<div class="container-fluid mt-4">
<h2 class="mb-4">Client File Reports</h2>
<h2 class="mb-4">Client RA Bills Reports</h2>
<div class="card p-4 shadow-sm mb-5">
<form method="POST">
<form method="POST" class="loading-form">
<label class="form-label fw-bold">RA Bill No</label>
<input type="text" name="RA_Bill_No" class="form-control mb-3" value="{{ ra_val }}" required>
@@ -15,16 +15,15 @@
Data</button>
</div>
<div class="col-md-6">
<button type="submit" name="action" value="download" class="btn btn-primary w-100">Download Excel
Report</button>
<button type="submit" name="action" value="download" class="btn btn-primary w-100">Download Excel Report</button>
</div>
</div>
</form>
</div>
{% if tables.tr or tables.mh or tables.dc or tables.laying %}
<div class="card shadow-sm p-3">
<h4 class="mb-3">Comparison Preview</h4>
<div class="card shadow-sm p-4">
<h4 class="mb-3">Table Preview</h4>
<ul class="nav nav-tabs" id="reportTabs" role="tablist">
<li class="nav-item">
@@ -32,6 +31,7 @@
type="button">Tr.Ex </button>
</li>
<li class="nav-item">
<<<<<<< HEAD
<button class="nav-link" id="mh-tab" data-bs-toggle="tab" data-bs-target="#mh" type="button">Mh.Ex
</button>
</li>
@@ -48,6 +48,18 @@
<button class="nav-link" id="laying-tab" data-bs-toggle="tab" data-bs-target="#laying" type="button">Laying
& Bedding </button>
>>>>>>> 1dceb640bd930c37888799f10f02fe90b219be67
=======
<button class="nav-link" id="mh-tab" data-bs-toggle="tab" data-bs-target="#mh" type="button">
Mh.Ex</button>
</li>
<li class="nav-item">
<button class="nav-link" id="dc-tab" data-bs-toggle="tab" data-bs-target="#dc" type="button">
MH & DC</button>
</li>
<li class="nav-item">
<button class="nav-link" id="laying-tab" data-bs-toggle="tab" data-bs-target="#laying"type="button">
Laying & Bedding </button>
>>>>>>> pankaj-dev
</ul>
<div class="tab-content mt-3" id="reportTabsContent">

View File

@@ -0,0 +1,84 @@
{% extends "base.html" %}
{% block content %}
<div class="container-fluid py-4">
<!-- Page Header -->
<div class="card shadow-sm border-0 mb-4">
<div class="card-body d-flex justify-content-between align-items-center">
<div>
<h2 class="fw-bold text-primary mb-1">
<i class="bi bi-pencil-square"></i>
Edit Record
</h2>
<small class="text-muted">
Model: <strong>{{ model|upper }}</strong> &nbsp;|&nbsp; Record ID: {{ record.id }}
</small>
</div>
<a href="{{ url_for('file_report.report_file') }}" class="btn btn-secondary">
<i class="bi bi-arrow-left"></i> Back to Report
</a>
</div>
</div>
<!-- Edit Form -->
<div class="card shadow-sm border-0">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">
<i class="bi bi-card-checklist"></i>
Update Record Details
</h5>
</div>
<div class="card-body">
<form method="POST">
<div class="row g-3">
{% for column in record.__table__.columns %}
{% if column.name != "id" %}
<div class="col-lg-4">
<label class="form-label fw-semibold">
{{ column.name.replace('_', ' ')|title }}
</label>
{% set value = record|attr(column.name) %}
{% if column.type.python_type.__name__ == 'bool' %}
<select name="{{ column.name }}" class="form-select">
<option value="true" {% if value %}selected{% endif %}>Yes</option>
<option value="false" {% if not value %}selected{% endif %}>No</option>
</select>
{% elif column.type.python_type.__name__ in ['int', 'float', 'Decimal'] %}
<input type="number" step="any" name="{{ column.name }}"
class="form-control" value="{{ value if value is not none else '' }}">
{% elif column.type.python_type.__name__ == 'date' %}
<input type="date" name="{{ column.name }}"
class="form-control" value="{{ value if value is not none else '' }}">
{% else %}
<input type="text" name="{{ column.name }}"
class="form-control" value="{{ value if value is not none else '' }}">
{% endif %}
</div>
{% endif %}
{% endfor %}
</div>
<div class="mt-4 d-flex justify-content-end gap-2">
<button type="submit" class="btn btn-primary">
<i class="bi bi-check-circle"></i> Save Changes
</button>
<a href="{{ url_for('file_report.report_file') }}" class="btn btn-secondary">
<i class="bi bi-x-circle"></i> Cancel
</a>
</div>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,236 @@
{% extends "base.html" %}
{% block content %}
<div class="container-fluid mt-4">
<div class="card shadow">
<div class="card-header bg-primary text-white">
<h4 class="mb-0">
<i class="bi bi-currency-rupee"></i>
Subcontractor Rate Master
</h4>
</div>
<div class="card-body">
<form method="POST">
<div class="row">
<!-- Subcontractor -->
<div class="col-md-4 mb-3">
<label class="form-label fw-bold">
Subcontractor
</label>
<select name="subcontractor_id"
class="form-select"
required>
<option value="">
Select Subcontractor
</option>
{% for sub in subcontractors %}
<option value="{{ sub.id }}">
{{ sub.subcontractor_name }}
</option>
{% endfor %}
</select>
</div>
<!-- Category -->
<div class="col-md-4 mb-3">
<label class="form-label fw-bold">
Category
</label>
<select name="category"
class="form-select"
required>
<option value="">
Select Category
</option>
<option value="TR_EX">
Trench Excavation
</option>
<option value="MH_EX">
Manhole Excavation
</option>
<option value="MH_DC">
Manhole & Domestic Chamber
</option>
<option value="LAYING">
Pipe Laying
</option>
</select>
</div>
<!-- Status -->
<div class="col-md-4 mb-3">
<label class="form-label fw-bold">
Status
</label>
<select name="status"
class="form-select">
<option value="Active">
Active
</option>
<option value="Inactive">
Inactive
</option>
</select>
</div>
</div>
<div class="row">
<!-- Item Code -->
<div class="col-md-3 mb-3">
<label class="form-label fw-bold">
Item Code
</label>
<input type="text"
name="item_code"
class="form-control"
placeholder="SM01"
required>
</div>
<!-- Item Name -->
<div class="col-md-5 mb-3">
<label class="form-label fw-bold">
Item Name
</label>
<input type="text"
name="item_name"
class="form-control"
placeholder="Soft Murum 0-1.5"
required>
</div>
<!-- Unit -->
<div class="col-md-2 mb-3">
<label class="form-label fw-bold">
Unit
</label>
<select name="unit"
class="form-select">
<option value="Cum">Cum</option>
<option value="Nos">Nos</option>
<option value="Rmt">Rmt</option>
<option value="Sqm">Sqm</option>
</select>
</div>
<!-- Rate -->
<div class="col-md-2 mb-3">
<label class="form-label fw-bold">
Rate (₹)
</label>
<input type="number"
step="0.01"
min="0"
name="rate"
class="form-control"
placeholder="0.00"
required>
</div>
</div>
<div class="row">
<!-- Effective From -->
<div class="col-md-3 mb-3">
<label class="form-label fw-bold">
Effective From
</label>
<input type="date"
name="effective_from"
class="form-control"
required>
</div>
<!-- Effective To -->
<div class="col-md-3 mb-3">
<label class="form-label fw-bold">
Effective To
</label>
<input type="date"
name="effective_to"
class="form-control">
</div>
</div>
<hr>
<div class="text-end">
<button type="reset"
class="btn btn-secondary">
<i class="bi bi-arrow-clockwise"></i>
Reset
</button>
<button type="submit"
class="btn btn-success">
<i class="bi bi-check-circle"></i>
Save Rate
</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,83 @@
{% extends "base.html" %}
{% block content %}
<div class="container-fluid mt-4">
<div class="row mb-4">
<div class="col-12">
<h3 class="fw-bold">
<i class="bi bi-gear-fill text-primary"></i>
Engineering Masters
</h3>
<hr>
</div>
</div>
<div class="row g-4">
<!-- Subcontractor Rate Master -->
<div class="col-lg-4 col-md-6">
<div class="card shadow h-100">
<div class="card-body text-center">
<i class="bi bi-cash-stack text-success"
style="font-size:55px;"></i>
<h5 class="mt-3">
Subcontractor Rate Master
</h5>
<p class="text-muted">
Manage subcontractor-wise rates.
</p>
<a href="{{ url_for('engineering.add_subcontractor_rates') }}"
class="btn btn-success">
<i class="bi bi-arrow-right-circle"></i>
Open Module
</a>
</div>
</div>
</div>
<!-- Client Rate Master -->
<div class="col-lg-4 col-md-6">
<div class="card shadow h-100">
<div class="card-body text-center">
<i class="bi bi-building text-primary"
style="font-size:55px;"></i>
<h5 class="mt-3">
Client Standard Rate Master
</h5>
<p class="text-muted">
Manage client standard rates.
</p>
<a href="{{ url_for('engineering.client_rates') }}"
class="btn btn-primary">
<i class="bi bi-arrow-right-circle"></i>
Open Module
</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

View File

@@ -1,41 +1,145 @@
{% extends "base.html" %}
{% block content %}
<h2 class="mb-4">Client File Import</h2>
<div class="card p-4 shadow-sm">
<div class="container-fluid py-4">
<form method="POST" enctype="multipart/form-data">
<!-- Page Header -->
<div class="row mb-4">
<!-- 1. SELECT SUBCONTRACTOR -->
<!-- <label class="form-label">Select Subcontractor vs Client</label>
<select name="subcontractor_id" id="subcontractor_id" class="form-select mb-3" required>
<option value="">-- Select Subcontractor --</option>
<div class="col-12">
{% for sc in subcontractors %}
<option value="{{ sc.id }}">{{ sc.subcontractor_name }}</option>
{% endfor %}
</select> -->
<div class="card border-0 shadow-sm bg-success text-white">
<!-- 2. FILE TYPE (MODEL NAME) -->
<!-- <label class="form-label">Select File Type</label>
<select name="file_type" id="file_type" class="form-select mb-3" required>
<option value="">-- Select File Type --</option>
<option value="">Sheet</option>
<option value="tr_ex_client">Tr. Ex</option>
<option value="mh_ex_client">Mh. Ex </option>
<option value="mh_dc_client">MH & DC </option>
<option value="">Laying Sheet</option>
</select> -->
<div class="card-body d-flex justify-content-between align-items-center">
<label class="form-label">RA Bill No</label>
<input type="text" name="RA_Bill_No" class="form-control mb-3" required>
<!-- 3. FILE UPLOAD -->
<label class="form-label">Choose File</label>
<input type="file" name="file" class="form-control mb-3" required>
<div>
<button class="btn btn-primary w-100">Upload</button>
</form>
<h3 class="fw-bold mb-1">
<i class="bi bi-building-fill-up me-2"></i>
Client RA Bill Import
</h3>
<p class="mb-0 opacity-75">
Upload the Client RA Bill Excel file for comparison.
</p>
</div>
<div class="display-5">
<i class="bi bi-file-earmark-excel-fill"></i>
</div>
</div>
</div>
</div>
</div>
<!-- Upload Card -->
<div class="row justify-content-center">
<div class="col-lg-7">
<div class="card border-0 shadow">
<div class="card-header bg-light">
<h5 class="mb-0 fw-bold text-success">
<i class="bi bi-upload me-2"></i>
Upload Details
</h5>
</div>
<div class="card-body p-4">
<form method="POST"
enctype="multipart/form-data"
class="loading-form"
onsubmit="showLoader('Reading Excel file...<br>Validating records...<br>Saving data to database...')">
<!-- RA Bill -->
<div class="mb-4">
<label class="form-label fw-semibold">
<i class="bi bi-receipt-cutoff text-success me-2"></i>
RA Bill No
<span class="text-danger">*</span>
</label>
<input
type="text"
name="RA_Bill_No"
class="form-control"
placeholder="Enter RA Bill Number"
required>
</div>
<!-- Upload -->
<div class="mb-4">
<label class="form-label fw-semibold">
<i class="bi bi-file-earmark-arrow-up text-primary me-2"></i>
Select Excel File
<span class="text-danger">*</span>
</label>
<input
type="file"
name="file"
class="form-control"
accept=".xlsx,.xls,.csv"
required>
<div class="form-text">
Supported Formats:
<strong>.xlsx</strong>,
<strong>.xls</strong>,
<strong>.csv</strong>
</div>
</div>
<hr>
<!-- Action Buttons -->
<div class="d-flex justify-content-between">
<button
type="submit"
class="btn btn-success px-5">
<i class="bi bi-cloud-upload-fill me-2"></i>
Upload File
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,32 +1,125 @@
{% extends "base.html" %}
{% block content %}
<h2 class="mb-4">Sub-Contractor File Import</h2>
<div class="card p-4 shadow-sm">
<div class="container-fluid py-4">
<form method="POST" enctype="multipart/form-data">
<!-- Page Header -->
<div class="row mb-4">
<div class="col-lg-12">
<div class="card border-0 shadow-sm bg-primary text-white">
<div class="card-body d-flex justify-content-between align-items-center">
<!-- 1. SELECT SUBCONTRACTOR -->
<label class="form-label">Select Subcontractor</label>
<select name="subcontractor_id" id="subcontractor_id" class="form-select mb-3" required>
<option value="">-- Select Subcontractor --</option>
<div>
<h3 class="mb-1 fw-bold">
<i class="bi bi-cloud-arrow-up-fill me-2"></i>
Sub-Contractor File Import
</h3>
{% for sc in subcontractors %}
<option value="{{ sc.id }}">{{ sc.subcontractor_name }}</option>
{% endfor %}
</select>
<p class="mb-0 opacity-75">
Upload RA Bill Excel file for the selected subcontractor.
</p>
</div>
<!-- 2. RA bill no -->
<label class="form-label">RA Bill No</label>
<input type="text" name="RA_Bill_No" class="form-control mb-3" required>
<div class="display-5">
<i class="bi bi-file-earmark-excel-fill"></i>
</div>
<!-- 3. FILE UPLOAD -->
<label class="form-label">Choose File</label>
<input type="file" name="file" class="form-control mb-3" required>
</div>
</div>
</div>
</div>
<button class="btn btn-primary w-100">Upload</button>
</form>
<!-- Upload Card -->
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card border-0 shadow">
<div class="card-header bg-light">
<h5 class="mb-0 fw-bold text-primary">
<i class="bi bi-upload me-2"></i>
Upload Details
</h5>
</div>
<div class="card-body p-4">
<form method="POST"
enctype="multipart/form-data"
class="loading-form"
onsubmit="showLoader('Reading Excel file...<br>Saving data to database...')">
<!-- Select Subcontractor -->
<div class="mb-4">
<label class="form-label fw-semibold">
<i class="bi bi-building me-1 text-primary"></i>
Select Subcontractor
<span class="text-danger">*</span>
</label>
<select name="subcontractor_id" id="subcontractor_id"
class="form-select" required>
<option value=""> -- Select Subcontractor -- </option>
{% for sc in subcontractors %}
<option value="{{ sc.id }}">{{ sc.subcontractor_name }} </option>
{% endfor %}
</select>
</div>
<!-- RA Bill No -->
<div class="mb-4">
<label class="form-label fw-semibold">
<i class="bi bi-receipt me-1 text-success"></i>
RA Bill No
<span class="text-danger">*</span>
</label>
<input type="text"
name="RA_Bill_No"
class="form-control"
placeholder="Enter RA Bill Number"
required>
</div>
<!-- File Upload -->
<div class="mb-4">
<label class="form-label fw-semibold">
<i class="bi bi-file-earmark-arrow-up me-1 text-warning"></i>
Upload Excel File
<span class="text-danger">*</span>
</label>
<input type="file"
name="file"
class="form-control"
accept=".xlsx,.xls,.csv"
required>
<div class="form-text">
Supported formats:
<strong>.xlsx</strong>,
<strong>.xls</strong>,
<strong>.csv</strong>
</div>
</div>
<hr>
<!-- Buttons -->
<div class="d-flex justify-content-between">
<button type="submit" class="btn btn-primary px-5">
<i class="bi bi-cloud-upload-fill me-2"></i>
Upload File
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -5,22 +5,9 @@
<h2 class="mb-4">Subcontractor vs Client Comparison</h2>
<!-- FLASH MESSAGES -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<div class="card p-4 shadow-sm">
<form method="POST">
<!-- SELECT SUBCONTRACTOR -->
<label class="form-label fw-semibold">Select Subcontractor</label>
<select name="subcontractor_id" id="subcontractor_id" class="form-select mb-3" required>

View File

@@ -8,75 +8,200 @@
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container-fluid vh-100">
<div class="row h-100 justify-content-center align-items-center">
<!-- Flash Messages -->
<div class="position-fixed top-0 end-0 p-3"
style="z-index:1080; width:min(95vw,420px);">
<!-- Increased column width -->
<div class="col-12 col-sm-10 col-md-8 col-lg-5 col-xl-4">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }} alert-dismissible fade show shadow notification-alert">
<strong>
{% if category=="success" %}
<i class="bi bi-check-circle-fill me-2"></i>Success
{% elif category=="danger" %}
<i class="bi bi-x-circle-fill me-2"></i>Error
{% elif category=="warning" %}
<i class="bi bi-exclamation-triangle-fill me-2"></i>Warning
{% else %}
<i class="bi bi-info-circle-fill me-2"></i>Information
{% endif %}
</strong>
<div>{{ message }}</div>
<div class="progress mt-2" style="height:4px;">
<div class="progress-bar progress-bar-striped progress-bar-animated timer-bar"
style="width:100%"></div>
</div>
<button class="btn-close"
data-bs-dismiss="alert">
</button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
</div>
<div class="container">
<div class="row justify-content-center align-items-center min-vh-100">
<div class="col-12 col-sm-10 col-md-8 col-lg-6 col-xl-5">
<div class="card shadow-lg border-0">
<!-- Increased padding -->
<div class="card-body p-5">
<!-- Branding -->
<div class="text-center mb-4">
<img src="{{ url_for('static', filename='images/lcepl.png') }}" alt="LCEPL Logo"
class="img-fluid mb-3" style="max-height:80px;">
<img src="{{ url_for('static', filename='images/lcepl.png') }}"
class="img-fluid mb-3"
style="max-height:100px;">
<h4 class="fw-bold mb-1">
Laxmi Civil Engineering Services Pvt Ltd
</h4>
<p class="text-muted mb-0">
Data Comparison Software Solapur(UGD)
</p>
<h3 class="fw-bold text-success">Laxmi Civil Engineering Services Pvt Ltd</h3>
<p class="text-muted">Data Comparison Software (UGD)</p>
</div>
<!-- Flash messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<!-- Login Form -->
<form method="POST">
<div class="mb-4">
<label class="form-label fw-semibold">User Name</label>
<input type="email" name="email" class="form-control " placeholder="Enter email"
required>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Username</label>
<div class="input-group">
<span class="input-group-text">
<i class="bi bi-envelope-fill"></i>
</span>
<input
type="text"
name="email"
class="form-control"
placeholder="Enter Domain Username"
autocomplete="username"
required>
</div>
</div>
<div class="mb-4">
<label class="form-label fw-semibold">Password</label>
<input type="password" name="password" class="form-control" placeholder="Enter password"
required>
<div class="input-group">
<span class="input-group-text">
<i class="bi bi-lock-fill"></i>
</span>
<input
type="password"
id="password"
name="password"
class="form-control"
placeholder="Enter Password"
required>
<button
type="button"
id="togglePassword"
class="input-group-text bg-white border-start-0"
style="cursor:pointer;">
<i class="bi bi-eye text-secondary"></i>
</button>
</div>
</div>
<button class="btn btn-success btn-lg w-100">
<button
class="btn btn-success w-100 btn-lg">
<i class="bi bi-box-arrow-in-right me-2"></i>
Login
</button>
<div class="text-center mt-3">
<a href="#">Forgot Password?</a>
</div>
</form>
</div>
</div>
</div>
<div class="text-center mt-3">
<small class="text-muted">
© 2026 Laxmi Civil Engineering Services Pvt Ltd
</small>
</div>
</div>
</div>
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
// Password Show / Hide
const togglePassword = document.getElementById("togglePassword");
const password = document.getElementById("password");
if (togglePassword && password) {
togglePassword.addEventListener("click", function () {
const type =
password.getAttribute("type") === "password"
? "text"
: "password";
password.setAttribute("type", type);
this.innerHTML =
type === "password"
? '<i class="bi bi-eye"></i>'
: '<i class="bi bi-eye-slash"></i>';
});
}
// Auto Close Notification after 5 sec
document.querySelectorAll(".notification-alert").forEach(function (alert) {
const progressBar = alert.querySelector(".timer-bar");
let width = 100;
const interval = setInterval(function () {
width -= 2;
progressBar.style.width = width + "%";
if (width <= 0) {
clearInterval(interval);
bootstrap.Alert
.getOrCreateInstance(alert)
.close();
}
}, 100);
});
});
</script>
</body>
</html>

View File

@@ -1,50 +1,170 @@
{% extends "base.html" %}
{% block content %}
<div class="card shadow-sm p-4">
<h4 class="mb-3">Add New Subcontractor</h4>
<div class="container-fluid">
<form action="{{ url_for('subcontractor.save_subcontractor') }}" method="POST">
<div class="mb-3">
<label class="form-label">Subcontractor Name:</label>
<input type="text" class="form-control" name="subcontractor_name" required>
<!-- Page Header -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h3 class="fw-bold mb-1">
<i class="bi bi-person-plus-fill text-success me-2"></i>
Add New Subcontractor
</h3>
<p class="text-muted mb-0">
Enter the subcontractor details below.
</p>
</div>
<div class="mb-3">
<label class="form-label">Contact Person Name:</label>
<input type="text" class="form-control" name="contact_person">
<a href="{{ url_for('subcontractor.subcontractor_list') }}"
class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-2"></i>
Back to List
</a>
</div>
<div class="card shadow border-0">
<div class="card-header bg-success text-white">
<h5 class="mb-0">
<i class="bi bi-building-fill-add me-2"></i>
Subcontractor Information
</h5>
</div>
<div class="mb-3">
<label class="form-label">Address:</label>
<textarea type="text" class="form-control" name="address"></textarea>
<div class="card-body">
<form action="{{ url_for('subcontractor.save_subcontractor') }}"
method="POST"
class="loading-form">
<div class="row g-4">
<!-- Subcontractor Name -->
<div class="col-md-6">
<label class="form-label fw-semibold">
<i class="bi bi-building me-1"></i>
Subcontractor Name
<span class="text-danger">*</span>
</label>
<input type="text"
class="form-control"
name="subcontractor_name"
placeholder="Enter Subcontractor Name"
required>
</div>
<!-- Contact Person -->
<div class="col-md-6">
<label class="form-label fw-semibold">
<i class="bi bi-person-fill me-1"></i>
Contact Person
</label>
<input type="text"
class="form-control"
name="contact_person"
placeholder="Enter Contact Person">
</div>
<!-- Mobile -->
<div class="col-md-6">
<label class="form-label fw-semibold">
<i class="bi bi-telephone-fill me-1"></i>
Mobile Number
</label>
<input type="text"
class="form-control"
name="mobile_no"
maxlength="10"
placeholder="Enter Mobile Number">
</div>
<!-- Email -->
<div class="col-md-6">
<label class="form-label fw-semibold">
<i class="bi bi-envelope-fill me-1"></i>
Email Address
</label>
<input type="email"
class="form-control"
name="email_id"
placeholder="Enter Email Address">
</div>
<!-- GST -->
<div class="col-md-6">
<label class="form-label fw-semibold">
<i class="bi bi-receipt-cutoff me-1"></i>
GST Number
</label>
<input type="text"
class="form-control"
name="gst_no"
placeholder="Enter GST Number">
</div>
<!-- PAN -->
<div class="col-md-6">
<label class="form-label fw-semibold">
<i class="bi bi-credit-card-2-front-fill me-1"></i>
PAN Number
</label>
<input type="text"
class="form-control"
name="pan_no"
placeholder="Enter PAN Number">
</div>
<!-- Address -->
<div class="col-12">
<label class="form-label fw-semibold">
<i class="bi bi-geo-alt-fill me-1"></i>
Address
</label>
<textarea class="form-control"
rows="4"
name="address"
placeholder="Enter Complete Address"></textarea>
</div>
</div>
<hr class="my-4">
<div class="d-flex justify-content-end gap-2">
<button type="reset"
class="btn btn-outline-warning">
<i class="bi bi-arrow-clockwise me-2"></i>
Reset
</button>
<a href="{{ url_for('subcontractor.subcontractor_list') }}"
class="btn btn-outline-secondary">
<i class="bi bi-x-circle me-2"></i>
Cancel
</a>
<button type="submit"
class="btn btn-success">
<i class="bi bi-check-circle-fill me-2"></i>
Save Subcontractor
</button>
</div>
</form>
</div>
<div class="mb-3">
<label class="form-label">Mobile No:</label>
<input type="text" class="form-control" name="mobile_no">
</div>
</div>
<div class="mb-3">
<label class="form-label">Email:</label>
<input type="email" class="form-control" name="email_id">
</div>
<div class="mb-3">
<label class="form-label">GST No:</label>
<input type="text" class="form-control" name="gst_no">
</div>
<div class="mb-3">
<label class="form-label">PAN No:</label>
<input type="text" class="form-control" name="pan_no">
</div>
<button class="btn btn-success">Save</button>
<a href="{{ url_for('subcontractor.subcontractor_list') }}" class="btn btn-secondary">Back</a>
</form>
</div>
{% endblock %}

View File

@@ -1,104 +1,378 @@
{% extends "base.html" %}
{% block content %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css">
<div class="container-fluid mt-3">
<h4 class="mb-4">Subcontractor Dashboard</h4>
<div class="row mb-3">
<div class="col-md-4">
<select id="subcontractor" class="form-control">
<option value="">Select Subcontractor</option>
{% for s in subcontractors %}
<option value="{{s.id}}">{{s.subcontractor_name}}</option>
{% endfor %}
</select>
</div>
<div class="col-md-4">
<select id="category" class="form-control">
<option value="">Select Category</option>
<option value="Soft Murum">Soft Murum</option>
<option value="Hard Murum">Hard Murum</option>
<option value="Soft Rock">Soft Rock</option>
<option value="Hard Rock">Hard Rock</option>
</select>
</div>
<div class="col-md-4">
<select id="ra_bill" class="form-control">
<option value="">RA Bill</option>
</select>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js"></script>
<div class="card shadow">
<div class="container-fluid py-4">
<!-- HEADER -->
<div class="card shadow border-0 mb-4">
<div class="card-body">
<canvas id="comparisonChart" height="120"></canvas>
<div class="d-flex justify-content-between align-items-center">
<div>
<h3 class="fw-bold text-primary">
<i class="bi bi-bar-chart-fill"></i>
RA Bill Analytics Dashboard
</h3>
<small class="text-muted">
Compare Quantity, Depth & RA Bill Analytics
</small>
</div>
<div>
<button class="btn btn-success" id="exportExcel">
<i class="bi bi-file-earmark-excel"></i>
Excel
</button>
<button class="btn btn-danger" id="exportPDF">
<i class="bi bi-file-earmark-pdf"></i>
PDF
</button>
</div>
</div>
</div>
</div>
<!-- FILTERS -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">
<i class="bi bi-funnel-fill"></i>
Filters
</h5>
</div>
<div class="card-body">
<div class="row g-3">
<!-- Contractor -->
<div class="col-lg-4">
<label class="form-label fw-bold"> Subcontractor </label>
<select class="form-select" id="subcontractor">
<option value="">--- select contractor ---</option>
{% for s in subcontractors %}
<option value="{{s.id}}">
{{s.subcontractor_name}}
</option>
{% endfor %}
</select>
</div>
<!-- Category -->
<div class="col-lg-4">
<label class="form-label fw-bold">Category</label>
<select class="form-select" id="category">
<option value="">--- select category ---</option>
<option value="trench_excavation">Trench Excavation</option>
<option value="manhole_excavation">Manhole Excavation</option>
<option value="Manhole_Domestic_Chamber">Manhole Domestic Chamber</option>
<option value="Laying">Pipe Laying</option>
</select>
</div>
<!-- RA Bill -->
<div class="col-lg-4">
<label class="form-label fw-bold"> RA Bills</label>
<select id="ra_bill" class="form-select" multiple></select>
</div>
</div>
<hr>
<!-- Search button -->
<button class="btn btn-primary" id="searchBtn">
<i class="bi bi-search"></i>Search
</button>
<!-- Reset button -->
<button class="btn btn-secondary" id="resetBtn">
<i class="bi bi-arrow-clockwise"></i>Reset
</button>
</div>
</div>
<!-- TABS -->
<div class="card shadow">
<div class="card-header">
<ul class="nav nav-pills">
<!--Bar Chart Tab -->
<li class="nav-item">
<button class="nav-link active"
data-bs-toggle="tab"
data-bs-target="#barTab">
<i class="bi bi-bar-chart"></i> Bar Chart
</button>
</li>
<!-- Data Table Tab -->
<li class="nav-item">
<button class="nav-link"
data-bs-toggle="tab"
data-bs-target="#tableTab">
<i class="bi bi-table"></i> Data Table
</button>
</li>
</ul>
</div>
<div class="card-body">
<div class="tab-content">
<!-- BAR TAB -->
<div class="tab-pane fade show active" id="barTab">
<div style="height:600px">
<canvas id="barChart"></canvas>
</div>
</div>
<!-- TABLE TAB -->
<div class="tab-pane fade" id="tableTab">
<div class="table-responsive">
<table class="table table-bordered table-hover" id="resultTable">
<thead class="table-dark">
<tr>
<th>Sr No</th>
<th>Strata Type & Depth</th>
<th class="text-end">Client Qty</th>
<th class="text-end">Sub Contractor Qty</th>
<th class="text-end">Difference</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
let chart;
let barChart;
let raBillChoice;
function loadChart() {
/* Load RA Bills */
function loadRABills(){
let subcontractor = document.getElementById("subcontractor").value
let category = document.getElementById("category").value
let ra_bill = document.getElementById("ra_bill").value
if (!raBillChoice)
return;
fetch(`/dashboard/api/subcontractor-chart?subcontractor=${subcontractor}&category=${category}&ra_bill=${ra_bill}`)
if (!subcontractor || !category) {
.then(res => res.json())
.then(data => {
raBillChoice.clearStore();
if (chart) {
chart.destroy()
}
return;
}
const ctx = document.getElementById("comparisonChart")
fetch(`/dashboard/api/get-ra-bills?subcontractor=${subcontractor}&category=${category}`)
.then(res => res.json())
.then(data => {
chart = new Chart(ctx, {
type: "bar",
data: {
labels: data.labels,
datasets: [{
label: "Subcontractor Quantity",
data: data.values,
backgroundColor: "#0d6efd"
}]
},
options: {
responsive: true,
plugins: {
legend: { display: true }
}
}
})
raBillChoice.clearStore();
})
let choices = [];
data.ra_bills.forEach(function(bill){
choices.push({
value: bill,
label: bill
});
});
raBillChoice.setChoices(
choices,
"value",
"label",
true
);
});
}
document.getElementById("subcontractor").addEventListener("change", loadChart)
document.getElementById("category").addEventListener("change", loadChart)
document.getElementById("ra_bill").addEventListener("change", loadChart)
/* Search */
function loadDashboard() {
const subcontractor = document.getElementById("subcontractor").value;
const category = document.getElementById("category").value;
let raBills = [];
if (raBillChoice) {
raBills = raBillChoice.getValue(true);
}
let apiUrl = "";
switch (category) {
case "trench_excavation":
apiUrl = "/dashboard/api/tr-analysis";
break;
case "manhole_excavation":
apiUrl = "/dashboard/api/mh-analysis";
break;
case "Manhole_Domestic_Chamber":
apiUrl = "/dashboard/api/mdc-analysis";
break;
case "Laying":
apiUrl = "/dashboard/api/laying-analysis";
break;
default:
alert("Please select category");
return;
}
fetch(`${apiUrl}?subcontractor=${subcontractor}&category=${category}&ra_bill=${raBills.join(",")}`)
.then(response => response.json())
.then(data => {
drawBar(data);
drawTable(data);
})
.catch(err => console.error(err));
}
/* BAR */
function drawBar(data) {
if (barChart) {
barChart.destroy();
}
const ctx = document.getElementById("barChart");
barChart = new Chart(ctx, {
type: "bar",
data: {
labels: data.labels,
datasets: [
{
label: "Client Qty",
data: data.client_qty,
backgroundColor: "#0d6efd",
borderColor: "#0d6efd",
borderWidth: 1
},
{
label: "Sub-Contractor Qty",
data: data.sub_qty,
backgroundColor: "#fd7e14",
borderColor: "#fd7e14",
borderWidth: 1
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: {
mode: "index",
intersect: false
},
plugins: {
title: {
display: true,
text: data.title
},
legend: {
position: "bottom"
}
},
scales: {
x: {
ticks: {
autoSkip: false,
maxRotation: 45,
minRotation: 45
}
},
y: {
beginAtZero: true,
title: {
display: true,
text: data.y_title
}
}
}
}
});
}
/* TABLE */
function drawTable(data){
let html='';
for(let i=0;i<data.labels.length;i++){
const clientQty = Number(data.client_qty[i] || 0);
const subQty = Number(data.sub_qty[i] || 0);
const diff = clientQty - subQty;
html+=`
<tr>
<td class="text-center">${i + 1}</td>
<td>${data.labels[i]}</td>
<td class="text-end">${data.client_qty[i]}</td>
<td class="text-end">${data.sub_qty[i]}</td>
<td class="text-end fw-bold ${diff >= 0 ? 'text-success' : 'text-danger'}">${diff.toFixed(2)}</td>
</tr>
`;
}
document.querySelector("#resultTable tbody").innerHTML = html;
}
/* EVENTS */
document.getElementById("subcontractor").addEventListener("change", loadRABills);
document.getElementById("category").addEventListener("change", loadRABills);
document.getElementById("searchBtn").addEventListener("click", loadDashboard);
document.getElementById("resetBtn").addEventListener("click", function () {location.reload();});
document.addEventListener("DOMContentLoaded", function () {
raBillChoice = new Choices("#ra_bill", {
removeItemButton: true,
searchEnabled: true,
searchPlaceholderValue: "Search RA Bills",
placeholder: true,
placeholderValue: "Select RA Bills",
shouldSort: false,
itemSelectText: ""
});
});
loadChart()
</script>

View File

@@ -1,117 +1,253 @@
{% extends "base.html" %}
{% block content %}
<div class="container my-4">
<h2 class="text-center mb-4">Generate Subcontractor Report</h2>
<div class="container-fluid py-4">
<!-- FORM -->
<div class="card shadow-sm p-3 p-md-4 mx-auto" style="max-width:600px;">
<form method="POST">
<div class="mb-3">
<label class="form-label fw-semibold">Select Subcontractor</label>
<select name="subcontractor_id" class="form-select" required>
<option value="">-- Select Subcontractor --</option>
{% for sc in subcontractors %}
<option value="{{ sc.id }}" {% if selected_sc_id==sc.id|string %}selected{% endif %}>
{{ sc.subcontractor_name }}
</option>
{% endfor %}
</select>
</div>
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" id="downloadAllSwitch" name="download_all" value="true"
{% if download_all %}checked{% endif %}>
<label class="form-check-label fw-bold text-primary">
Download All RA Bills
</label>
</div>
<div class="mb-4" id="ra_bill_container">
<label class="form-label fw-semibold">RA Bill Number</label>
<input type="text" name="ra_bill_no" id="ra_bill_input" class="form-control"
value="{{ ra_bill_no or '' }}">
</div>
<div class="row g-2">
<div class="col-12 col-md-6">
<button class="btn btn-outline-primary w-100" name="action" value="preview">
Preview Data
</button>
</div>
<div class="col-12 col-md-6">
<button class="btn btn-primary w-100" name="action" value="download">
Download Excel
</button>
<!-- Page Header -->
<div class="card shadow-sm border-0 mb-4">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h2 class="fw-bold text-primary mb-1">
<i class="bi bi-file-earmark-bar-graph"></i>
Subcontractor Report
</h2>
<small class="text-muted">
View, Filter, Edit and Delete Subcontractor Records
</small>
</div>
</div>
</div>
</div>
</form>
<!-- Filter Card -->
<div class="card shadow-sm border-0">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">
<i class="bi bi-funnel-fill"></i>
Report Filters
</h5>
</div>
<div class="card-body">
<form method="POST" class="loading-form">
<div class="row g-3">
<div class="col-lg-3">
<label class="form-label fw-semibold">
Subcontractor
<span class="text-danger">*</span>
</label>
<select name="subcontractor_id" class="form-select" required>
<option value="">--- Select Contractor ---</option>
{% for sc in subcontractors %}
<option value="{{ sc.id }}"
{% if selected_sc_id|string == sc.id|string %}selected{% endif %}>
{{ sc.subcontractor_name }}
</option>
{% endfor %}
</select>
</div>
<div class="col-lg-3">
<label class="form-label fw-semibold"> RA Bill No</label>
<input
type="text"
name="ra_bill_no"
class="form-control"
placeholder="Enter RA Bill"
value="{{ selected_ra_bill or '' }}">
</div>
<div class="col-lg-3">
<label class="form-label fw-semibold"> Location </label>
<input
type="text"
name="location"
class="form-control"
placeholder="Project Location"
value="{{ selected_location or '' }}">
</div>
<div class="col-lg-3">
<label class="form-label fw-semibold">Work Category</label>
<select name="category" class="form-select">
<option value="all">All Categories</option>
<option value="tr"
{% if request.form.get('category')=='tr' %}selected{% endif %}>
Trench Excavation
</option>
<option value="mh"
{% if request.form.get('category')=='mh' %}selected{% endif %}>
Manhole Excavation
</option>
<option value="dc"
{% if request.form.get('category')=='dc' %}selected{% endif %}>
Manhole Domestic Chamber
</option>
<option value="laying"
{% if request.form.get('category')=='laying' %}selected{% endif %}>
Pipe Laying
</option>
</select>
</div>
</div>
<div class="mt-4 d-flex justify-content-end gap-2">
<!-- Preview -->
<button type="submit" name="action" value="preview"class="btn btn-primary">
<i class="bi bi-search"></i>
Preview Report
</button>
<!-- Download -->
<div class="btn-group">
<button type="button" class="btn btn-success dropdown-toggle"data-bs-toggle="dropdown">
<i class="bi bi-download"></i>
Download Report
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<button class="dropdown-item"type="submit" name="action" value="excel">
<i class="bi bi-file-earmark-excel text-success"></i>
Download Excel
</button>
</li>
<li>
<button class="dropdown-item" type="submit" name="action"
value="excel_all">
<i class="bi bi-file-earmark-spreadsheet text-primary"></i>
Download All Excel
</button>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<button class="dropdown-item"type="submit"name="action" value="pdf">
<i class="bi bi-file-earmark-pdf text-danger"></i>
PDF (Coming Soon)
</button>
</li>
</ul>
</div>
<button type="reset" class="btn btn-secondary" id="resetBtn">
<i class="bi bi-arrow-clockwise"></i>
Reset
</button>
</div>
</form>
</div>
</div>
{% if tables %}
<!-- REPORT PREVIEW -->
<div class="mt-5">
<!-- Tabs -->
<div class="card shadow-sm border-0 mt-4">
<div class="card-header bg-light">
<ul class="nav nav-pills">
<li class="nav-item">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#abstract">
<i class="bi bi-file-earmark-text"></i> Abstract
</button>
</li>
<h3 class="text-center mb-3">Report Preview</h3>
<li class="nav-item">
<button class="nav-link " data-bs-toggle="tab" data-bs-target="#tr">
<i class="bi bi-cone-striped"></i> Trench Excavation
</button>
</li>
<div class="card shadow-sm">
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#mh">
<i class="bi bi-nut"></i> Manhole Excavation
</button>
</li>
<!-- MOBILE SCROLLABLE TABS -->
<div class="card-header p-0">
<ul class="nav nav-tabs flex-nowrap overflow-auto">
<li class="nav-item">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#tr">
Trench Excavation
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#dc">
<i class="bi bi-grid-3x3"></i> Manhole & Domestic Chambers Construction
</button>
</li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#laying">
<i class="bi bi-bezier2"></i> Pipe Laying
</button>
</li>
</ul>
</div>
<div class="card-body">
<div class="tab-content">
<div class="tab-pane fade show active" id="abstract">
{{ abstract_html|safe }}
</div>
<!-- Trench -->
<div class="tab-pane fade "id="tr">
<div class="mb-3">
<button onclick="deleteSelected('tr')" class="btn btn-danger">
<i class="bi bi-trash"></i> Delete Selected
</button>
</li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#mh">
Manhole Excavation
</button>
</li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#dc">
Manhole & Domestic Chambers
</button>
</li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#laying">
Pipe Laying
</button>
</li>
</ul>
</div>
<!-- TAB CONTENT -->
<div class="card-body tab-content">
<div class="tab-pane fade show active" id="tr">
<div class="table-responsive overflow-auto">
{{ tables.tr | safe }}
</div>
<div class="table-responsive border rounded shadow-sm">
{{ tables.tr|safe }}
</div>
</div>
<!-- MH -->
<div class="tab-pane fade" id="mh">
<div class="table-responsive overflow-auto">
{{ tables.mh | safe }}
<div class="mb-3">
<button onclick="deleteSelected('mh')" class="btn btn-danger">
<i class="bi bi-trash"></i>Delete Selected
</button>
</div>
<div class="table-responsive border rounded shadow-sm">
{{ tables.mh|safe }}
</div>
</div>
<!-- DC -->
<div class="tab-pane fade" id="dc">
<div class="table-responsive overflow-auto">
{{ tables.dc | safe }}
<div class="mb-3">
<button onclick="deleteSelected('dc')"class="btn btn-danger">
<i class="bi bi-trash"></i>
Delete Selected
</button>
</div>
</div>
<div class="table-responsive border rounded shadow-sm">
{{ tables.dc|safe }}
</div>
</div>
<!-- Laying -->
<div class="tab-pane fade" id="laying">
<div class="table-responsive overflow-auto">
{{ tables.laying | safe }}
<div class="mb-3">
<button onclick="deleteSelected('laying')"
class="btn btn-danger">
<i class="bi bi-trash"></i>
Delete Selected
</button>
</div>
<div class="table-responsive border rounded shadow-sm">
{{ tables.laying|safe }}
</div>
</div>
</div>
</div>
</div>
@@ -119,24 +255,90 @@
</div>
<!-- BOOTSTRAP-ONLY JS -->
<script>
function toggleRAInput() {
const checkbox = document.getElementById("downloadAllSwitch");
const input = document.getElementById("ra_bill_input");
const container = document.getElementById("ra_bill_container");
// Reset
document.getElementById("resetBtn").addEventListener("click", function () {location.reload();});
if (checkbox.checked) {
input.value = "";
input.disabled = true;
container.classList.add("opacity-50");
} else {
input.disabled = false;
container.classList.remove("opacity-50");
}
// DATATABLE
$(document).ready(function () {
$('.datatable').DataTable({
pageLength: 10,
dom: 'Bfrtip',
buttons: ['copy', 'csv', 'excel', 'print']
});
setTimeout(() => {
$("table thead tr th:first-child").html('<input type="checkbox" id="select-all">');
}, 500);
});
// SELECT ALL
$(document).on("change", "#select-all", function () {
$(".row-check").prop("checked", this.checked);
});
// GET IDS
function getSelectedIds() {
let ids = [];
$(".row-check:checked").each(function () {
ids.push($(this).data("id"));
});
return ids;
}
document.addEventListener("DOMContentLoaded", toggleRAInput);
document.getElementById("downloadAllSwitch").addEventListener("change", toggleRAInput);
// BULK DELETE
function deleteSelected(model) {
let ids = getSelectedIds();
if (ids.length === 0) return alert("Select records");
if (!confirm(`Delete ${ids.length} record(s)?`)) return;
fetch("/file/delete_records", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ model: model, ids: ids })
})
.then(res => res.json().then(data => ({ ok: res.ok, data })))
.then(({ ok, data }) => {
if (ok && data.status === "success") {
alert("Deleted Successfully");
location.reload();
} else {
alert("Delete failed: " + (data.message || "Unknown error"));
}
})
.catch(err => {
alert("Delete request failed: " + err);
});
}
// SINGLE DELETE
$(document).on("click", ".delete-btn", function () {
let id = $(this).data("id");
let model = $(this).data("model");
if (!confirm("Are you sure you want to delete this record?")) return;
fetch("/file/delete_records", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ model: model, ids: [id] })
})
.then(res => res.json().then(data => ({ ok: res.ok, data })))
.then(({ ok, data }) => {
if (ok && data.status === "success") {
alert("Deleted Successfully");
location.reload();
} else {
alert("Delete failed: " + (data.message || "Unknown error"));
}
})
.catch(err => {
alert("Delete request failed: " + err);
});
});
</script>
{% endblock %}