937 lines
30 KiB
JavaScript
937 lines
30 KiB
JavaScript
import React, { useEffect, useState } from "react";
|
|
import { fetchStandardParameters, downloadFile } from "../../../APIServices/ReportRunnerAPI";
|
|
import ReportBuilderService from "../../../APIServices/ReportBuilderService";
|
|
import { toast } from "react-toastify";
|
|
import './ReportrunnerEdit.css'
|
|
import { saveAs } from 'file-saver';
|
|
|
|
import axios from 'axios';
|
|
import { useNavigate, useParams } from 'react-router-dom';
|
|
|
|
const ReportRunner2Edit = () => {
|
|
const navigate = useNavigate();
|
|
const { id } = useParams();
|
|
|
|
// Report data state
|
|
const [reportName, setReportName] = useState("Sample Report");
|
|
const [reportId, setReportId] = useState(id || null);
|
|
|
|
// Date parameters state
|
|
const [dateParam, setDateParam] = useState(true);
|
|
const [selectDateType, setSelectDateType] = useState("");
|
|
const [fromDate, setFromDate] = useState("");
|
|
const [toDate, setToDate] = useState("");
|
|
const [fromDateQuery, setFromDateQuery] = useState("");
|
|
const [toDateQuery, setToDateQuery] = useState("");
|
|
|
|
// Standard parameters state
|
|
const [dynamicFields, setDynamicFields] = useState([]);
|
|
const [dynamicFormValues, setDynamicFormValues] = useState({});
|
|
|
|
// Adhoc parameters state
|
|
const [adhocList, setAdhocList] = useState([]);
|
|
const [adhocParams, setAdhocParams] = useState([
|
|
{ andor: "AND", fields_name: "", condition: "=", value: "" },
|
|
]);
|
|
|
|
// Condition options
|
|
const conditionOptions = ["=", "!=", "<", ">", "<=", ">=", "LIKE", "BETWEEN", "IN"];
|
|
const andOrOptions = ["AND", "OR", "NOT"];
|
|
|
|
// Results state
|
|
const [rows, setRows] = useState([]);
|
|
const [filterRows, setFilterRows] = useState([]);
|
|
const [filtered, setFiltered] = useState(false);
|
|
const [headers, setHeaders] = useState([]);
|
|
|
|
// Query state
|
|
const [SQLQuery, setSQLQuery] = useState('');
|
|
const [dateKey, setDateKey] = useState('createdat');
|
|
const [formattedAdhocParameters, setFormattedAdhocParameters] = useState('');
|
|
const [selectedValues, setSelectedValues] = useState({});
|
|
|
|
// Loading state
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [error, setError] = useState(null);
|
|
|
|
// Get today's date
|
|
const todayDate = new Date().toISOString().slice(0, 10);
|
|
|
|
// Add this at the beginning of the component with other state variables
|
|
const [showExportDropdown, setShowExportDropdown] = useState(false);
|
|
|
|
useEffect(() => {
|
|
// Fetch report details
|
|
if (reportId) {
|
|
fetchReportDetails(reportId);
|
|
}
|
|
|
|
// Set default date to today
|
|
handleDateSelect('Today');
|
|
|
|
// Run query after a delay to allow data to load
|
|
const timer = setTimeout(() => {
|
|
runQuery();
|
|
}, 2000);
|
|
|
|
return () => clearTimeout(timer);
|
|
}, [reportId]);
|
|
|
|
useEffect(() => {
|
|
// Function to handle clicks outside the dropdown
|
|
const handleClickOutside = (event) => {
|
|
// Get reference to the dropdown container
|
|
const dropdownContainer = document.getElementById('export-dropdown-container');
|
|
|
|
// Close dropdown if click is outside the container
|
|
if (dropdownContainer && !dropdownContainer.contains(event.target)) {
|
|
setShowExportDropdown(false);
|
|
}
|
|
};
|
|
|
|
// Function to handle escape key press
|
|
const handleEscapeKey = (event) => {
|
|
if (event.key === 'Escape') {
|
|
setShowExportDropdown(false);
|
|
}
|
|
};
|
|
|
|
// Add event listeners if dropdown is open
|
|
if (showExportDropdown) {
|
|
document.addEventListener('mousedown', handleClickOutside);
|
|
document.addEventListener('keydown', handleEscapeKey);
|
|
}
|
|
|
|
// Clean up event listeners when dropdown closes or component unmounts
|
|
return () => {
|
|
document.removeEventListener('mousedown', handleClickOutside);
|
|
document.removeEventListener('keydown', handleEscapeKey);
|
|
};
|
|
}, [showExportDropdown]);
|
|
|
|
const fetchReportDetails = async (id) => {
|
|
try {
|
|
setIsLoading(true);
|
|
// Fetch report details
|
|
const response = await ReportBuilderService.getrbDetailsById(id);
|
|
const data = response?.data;
|
|
if (!data) {
|
|
throw new Error("No data received from the API");
|
|
}
|
|
console.log("Report details received:", data);
|
|
setReportName(data.reportName);
|
|
// Parse builder line data
|
|
const builderLine = data.rpt_builder2_lines?.[0];
|
|
if (builderLine) {
|
|
const lineData = JSON.parse(builderLine.model)?.[0];
|
|
console.log("lineData: ",lineData);
|
|
if (lineData) {
|
|
setAdhocList(lineData.adhoc_param_html || []);
|
|
setDateParam(lineData.date_param_req || false);
|
|
setSQLQuery(lineData.url || "");
|
|
// Set dynamic fields from standard parameters
|
|
const dynamicFields = lineData.std_param_html || [];
|
|
setDynamicFields(dynamicFields);
|
|
// Initialize dynamic form values
|
|
const initialFormValues = dynamicFields.reduce((acc, field) => {
|
|
acc[field] = "";
|
|
return acc;
|
|
}, {});
|
|
setDynamicFormValues(initialFormValues);
|
|
// Fetch data if a URL exists
|
|
if (lineData.url) {
|
|
fetchData(lineData.url);
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching report details:", error);
|
|
setError("Error loading report details");
|
|
toast.error("Failed to load report details");
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
|
|
const fetchData = async (url) => {
|
|
try {
|
|
const response = await ReportBuilderService.getAllDetailsByurl(url);
|
|
console.log("responce of data fetch from url:", response.data)
|
|
const data = response.data?.body ? JSON.parse(response.data.body) : [];
|
|
setRows(data);
|
|
setFilterRows(data);
|
|
|
|
// Set headers from the first row if available
|
|
if (data.length > 0) {
|
|
setHeaders(Object.keys(data[0]));
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching data:", error);
|
|
setError("Error loading data");
|
|
}
|
|
};
|
|
|
|
// Handle changes to adhoc parameters
|
|
const handleAdhocChange = (index, field, value) => {
|
|
const updatedParams = [...adhocParams];
|
|
updatedParams[index][field] = value;
|
|
setAdhocParams(updatedParams);
|
|
};
|
|
|
|
// Add a new adhoc parameter row
|
|
const addAdhocRow = () => {
|
|
const lastRow = adhocParams[adhocParams.length - 1];
|
|
|
|
// Only add a new row if the last row has a field name
|
|
if (lastRow && lastRow.fields_name !== '') {
|
|
// Format the adhoc parameters for the query
|
|
let formattedString = '';
|
|
for (const condition of adhocParams) {
|
|
const { andor, fields_name, condition: cond, value } = condition;
|
|
formattedString += ` ${andor} ${fields_name} ${cond} '${value}' `;
|
|
}
|
|
setFormattedAdhocParameters(formattedString);
|
|
|
|
// Add a new row
|
|
setAdhocParams([
|
|
...adhocParams,
|
|
{ andor: "AND", fields_name: "", condition: "=", value: "" },
|
|
]);
|
|
|
|
// Update selected values for filtering
|
|
selectColumn(adhocParams);
|
|
}
|
|
};
|
|
|
|
// Delete an adhoc parameter row
|
|
const deleteAdhocRow = (index) => {
|
|
if (adhocParams.length > 1) {
|
|
// Get the item to be deleted
|
|
const deletedItem = adhocParams[index];
|
|
|
|
// Remove the item from the adhocParams array
|
|
const updatedParams = adhocParams.filter((_, i) => i !== index);
|
|
setAdhocParams(updatedParams);
|
|
|
|
// Update selected values
|
|
const updatedSelectedValues = { ...selectedValues };
|
|
const columnName = deletedItem.fields_name;
|
|
|
|
if (updatedSelectedValues[columnName]) {
|
|
const value = deletedItem.value;
|
|
const indexInArray = updatedSelectedValues[columnName].indexOf(value);
|
|
if (indexInArray !== -1) {
|
|
updatedSelectedValues[columnName].splice(indexInArray, 1);
|
|
|
|
// If the array is now empty, remove the property
|
|
if (updatedSelectedValues[columnName].length === 0) {
|
|
delete updatedSelectedValues[columnName];
|
|
}
|
|
}
|
|
}
|
|
|
|
setSelectedValues(updatedSelectedValues);
|
|
filterRowsBySelectedValues(updatedSelectedValues);
|
|
}
|
|
};
|
|
|
|
// Handle dynamic form values change
|
|
const handleDynamicFormChange = (field, value) => {
|
|
setDynamicFormValues({
|
|
...dynamicFormValues,
|
|
[field]: value
|
|
});
|
|
};
|
|
|
|
// Handle date type selection
|
|
const handleDateSelect = (dateType) => {
|
|
setSelectDateType(dateType);
|
|
setFromDateQuery(null);
|
|
setToDateQuery(null);
|
|
|
|
const currentDate = new Date();
|
|
let fromDateValue, toDateValue;
|
|
|
|
switch (dateType) {
|
|
case 'Today':
|
|
fromDateValue = new Date();
|
|
toDateValue = new Date();
|
|
break;
|
|
|
|
case 'This Week':
|
|
// Calculate this week (Monday to Sunday)
|
|
const dayOfWeek = currentDate.getDay();
|
|
const daysToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
|
|
|
fromDateValue = new Date(currentDate);
|
|
fromDateValue.setDate(currentDate.getDate() - daysToMonday);
|
|
|
|
toDateValue = new Date(fromDateValue);
|
|
toDateValue.setDate(fromDateValue.getDate() + 6);
|
|
break;
|
|
|
|
case 'Last Week':
|
|
// Calculate last week
|
|
const lastWeekDayOfWeek = currentDate.getDay();
|
|
const lastWeekDaysToMonday = lastWeekDayOfWeek === 0 ? 6 : lastWeekDayOfWeek - 1;
|
|
|
|
fromDateValue = new Date(currentDate);
|
|
fromDateValue.setDate(currentDate.getDate() - lastWeekDaysToMonday - 7);
|
|
|
|
toDateValue = new Date(fromDateValue);
|
|
toDateValue.setDate(fromDateValue.getDate() + 6);
|
|
break;
|
|
|
|
case 'This Month':
|
|
// First day of current month
|
|
fromDateValue = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1);
|
|
// Last day of current month
|
|
toDateValue = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0);
|
|
break;
|
|
|
|
case 'Last Month':
|
|
// First day of previous month
|
|
fromDateValue = new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1);
|
|
// Last day of previous month
|
|
toDateValue = new Date(currentDate.getFullYear(), currentDate.getMonth(), 0);
|
|
break;
|
|
|
|
case 'This Year':
|
|
// First day of current year
|
|
fromDateValue = new Date(currentDate.getFullYear(), 0, 1);
|
|
// Last day of current year
|
|
toDateValue = new Date(currentDate.getFullYear(), 11, 31);
|
|
break;
|
|
|
|
case 'Last Year':
|
|
// First day of previous year
|
|
fromDateValue = new Date(currentDate.getFullYear() - 1, 0, 1);
|
|
// Last day of previous year
|
|
toDateValue = new Date(currentDate.getFullYear() - 1, 11, 31);
|
|
break;
|
|
|
|
default:
|
|
fromDateValue = null;
|
|
toDateValue = null;
|
|
}
|
|
|
|
if (fromDateValue) {
|
|
const fromDateString = fromDateValue.toISOString().substring(0, 10);
|
|
setFromDate(fromDateString);
|
|
setFromDateQuery(fromDateString);
|
|
} else {
|
|
setFromDate('');
|
|
setFromDateQuery('');
|
|
}
|
|
|
|
if (toDateValue) {
|
|
const toDateString = toDateValue.toISOString().substring(0, 10);
|
|
setToDate(toDateString);
|
|
setToDateQuery(toDateString);
|
|
} else {
|
|
setToDate('');
|
|
setToDateQuery('');
|
|
}
|
|
};
|
|
|
|
// Run the query
|
|
const runQuery = () => {
|
|
console.log("Dynamic form values:", dynamicFormValues);
|
|
console.log("Date range:", fromDate, toDate);
|
|
|
|
let query = SQLQuery || '';
|
|
|
|
// Add standard parameters to the query
|
|
if (Object.keys(dynamicFormValues).length > 0) {
|
|
Object.keys(dynamicFormValues).forEach((key) => {
|
|
if (dynamicFormValues[key] !== null && dynamicFormValues[key] !== '') {
|
|
query += ` AND ${key} = '${dynamicFormValues[key]}'`;
|
|
}
|
|
});
|
|
|
|
// Update selected values for filtering
|
|
selectColumn(dynamicFormValues);
|
|
}
|
|
|
|
// Add date range to the query if date parameter is required
|
|
if (dateParam) {
|
|
// Determine the correct date key based on adhoc list
|
|
let tempDateKey = 'createdat';
|
|
adhocList.forEach(key => {
|
|
if (key.includes('created_at')) tempDateKey = 'created_at';
|
|
if (key.includes('createdAt')) tempDateKey = 'createdAt';
|
|
});
|
|
setDateKey(tempDateKey);
|
|
|
|
if (fromDate && toDate) {
|
|
const fromDateObj = new Date(fromDate);
|
|
const toDateObj = new Date(toDate);
|
|
|
|
query += ` AND ${tempDateKey} BETWEEN '${fromDateObj.toISOString().split('T')[0]}' AND '${toDateObj.toISOString().split('T')[0]}'`;
|
|
}
|
|
}
|
|
|
|
// Format and add adhoc parameters to the query
|
|
let formattedString = '';
|
|
for (const condition of adhocParams) {
|
|
// Only add conditions that have a field name selected
|
|
if (condition.fields_name) {
|
|
const { andor, fields_name, condition: cond, value } = condition;
|
|
formattedString += ` ${andor} ${fields_name} ${cond} '${value}' `;
|
|
}
|
|
}
|
|
setFormattedAdhocParameters(formattedString);
|
|
|
|
// Add adhoc parameters to the query
|
|
if (formattedString) {
|
|
query += formattedString;
|
|
}
|
|
|
|
// Update selected values for filtering based on adhoc parameters
|
|
selectColumn(adhocParams.filter(param => param.fields_name));
|
|
|
|
console.log("Final query:", query);
|
|
|
|
// Make API request
|
|
// axios.post('/api/report-runner/run', { query })
|
|
// .then((response) => {
|
|
// const data = response.data;
|
|
|
|
// if (data && data.length > 0) {
|
|
// setRows(data);
|
|
// setFilterRows(data);
|
|
// setHeaders(Object.keys(data[0]));
|
|
// toast.success("Query executed successfully");
|
|
// } else {
|
|
// toast.warning("No data returned");
|
|
// }
|
|
// })
|
|
// .catch((error) => {
|
|
// console.error("Error running query:", error);
|
|
// toast.error("Error running the query");
|
|
// });
|
|
};
|
|
|
|
// Export file function
|
|
const exportFile = (format) => {
|
|
try {
|
|
const dataToExport = filtered ? filterRows : rows;
|
|
|
|
if (!dataToExport || dataToExport.length === 0) {
|
|
toast.warning("No data to export");
|
|
return;
|
|
}
|
|
|
|
const name = reportName.replace(/\s+/g, '_');
|
|
const timestamp = new Date().toISOString().replace(/[-:T.]/g, '_').slice(0, 17);
|
|
const fileName = `${name}_${timestamp}`;
|
|
|
|
// Show loading toast
|
|
const loadingToast = toast.info("Preparing file for export...", { autoClose: false });
|
|
|
|
// Use the imported downloadFile service
|
|
downloadFile(format, dataToExport, fileName);
|
|
|
|
// Success message
|
|
setTimeout(() => {
|
|
toast.dismiss(loadingToast);
|
|
toast.success(`File exported as ${format.toUpperCase()} successfully`);
|
|
}, 1000);
|
|
|
|
} catch (error) {
|
|
console.error("Error in export function:", error);
|
|
toast.error("Error exporting file");
|
|
}
|
|
};
|
|
|
|
// Get table headers
|
|
const getHeaders = () => {
|
|
if (!rows || rows.length === 0) return [];
|
|
|
|
const headerSet = new Set();
|
|
rows.forEach(row => {
|
|
Object.keys(row).forEach(key => headerSet.add(key));
|
|
});
|
|
|
|
return Array.from(headerSet);
|
|
};
|
|
|
|
// Get filtered table headers
|
|
const getFilteredHeaders = () => {
|
|
if (!filterRows || filterRows.length === 0) return [];
|
|
|
|
const headerSet = new Set();
|
|
filterRows.forEach(row => {
|
|
Object.keys(row).forEach(key => headerSet.add(key));
|
|
});
|
|
|
|
return Array.from(headerSet);
|
|
};
|
|
|
|
// Check if a value is a date
|
|
const isDate = (value) => {
|
|
if (!value) return false;
|
|
|
|
if (value instanceof Date) return true;
|
|
|
|
if (typeof value === 'object' &&
|
|
value.year !== undefined &&
|
|
value.monthValue !== undefined &&
|
|
value.dayOfMonth !== undefined) {
|
|
return true;
|
|
}
|
|
|
|
const date = new Date(value);
|
|
return !isNaN(date.getTime());
|
|
};
|
|
|
|
// Format a date for display
|
|
const formatDate = (value) => {
|
|
if (!value) return '';
|
|
|
|
if (typeof value === 'object' &&
|
|
value.year !== undefined &&
|
|
value.monthValue !== undefined &&
|
|
value.dayOfMonth !== undefined) {
|
|
// Handle Java-style date objects
|
|
const { year, monthValue, dayOfMonth, hour = 0, minute = 0, second = 0 } = value;
|
|
const date = new Date(year, monthValue - 1, dayOfMonth, hour, minute, second);
|
|
return date.toLocaleString();
|
|
}
|
|
|
|
// Standard JS date
|
|
return new Date(value).toLocaleString();
|
|
};
|
|
|
|
// Process data for filtering
|
|
const selectColumn = (data) => {
|
|
const newSelectedValues = { ...selectedValues };
|
|
|
|
if (Array.isArray(data)) {
|
|
// Handle array of objects (like adhoc parameters)
|
|
data.forEach(item => {
|
|
const { fields_name, value } = item;
|
|
|
|
if (fields_name && fields_name.trim() !== '') {
|
|
if (!newSelectedValues[fields_name]) {
|
|
newSelectedValues[fields_name] = [];
|
|
}
|
|
|
|
if (value !== null && value.trim() !== '') {
|
|
if (!newSelectedValues[fields_name].includes(value)) {
|
|
newSelectedValues[fields_name].push(value);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
} else if (typeof data === 'object') {
|
|
// Handle object (like dynamic form values)
|
|
Object.keys(data).forEach(key => {
|
|
const value = data[key];
|
|
|
|
if (!newSelectedValues[key]) {
|
|
newSelectedValues[key] = [];
|
|
}
|
|
|
|
if (value !== null && value.trim && value.trim() !== '') {
|
|
if (!newSelectedValues[key].includes(value)) {
|
|
newSelectedValues[key].push(value);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
setSelectedValues(newSelectedValues);
|
|
filterRowsBySelectedValues(newSelectedValues);
|
|
};
|
|
|
|
// Filter rows based on selected values
|
|
const filterRowsBySelectedValues = (values = selectedValues) => {
|
|
const filteredRows = [];
|
|
|
|
for (const row of rows) {
|
|
let isMatch = true;
|
|
|
|
// Check each column in the selected values
|
|
for (const columnName in values) {
|
|
if (values.hasOwnProperty(columnName) && row.hasOwnProperty(columnName)) {
|
|
const selectedValuesForColumn = values[columnName];
|
|
const rowValue = row[columnName];
|
|
|
|
if (typeof rowValue === 'boolean') {
|
|
// Handle boolean values
|
|
if (selectedValuesForColumn.length === 0) continue;
|
|
|
|
const selectedBooleanValue = selectedValuesForColumn[0] === 'true';
|
|
if (selectedBooleanValue !== rowValue) {
|
|
isMatch = false;
|
|
break;
|
|
}
|
|
} else {
|
|
// Handle other data types
|
|
const convertedValues = selectedValuesForColumn.map(value => {
|
|
if (typeof rowValue === 'number') {
|
|
return parseFloat(value);
|
|
}
|
|
return value;
|
|
});
|
|
|
|
if (!convertedValues.includes(rowValue)) {
|
|
isMatch = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check date range if both dates are provided
|
|
if (fromDateQuery && toDateQuery && isMatch) {
|
|
const from = new Date(fromDateQuery);
|
|
const to = new Date(toDateQuery);
|
|
|
|
// Set hours to 0 for proper comparison
|
|
from.setHours(0, 0, 0, 0);
|
|
to.setHours(23, 59, 59, 999);
|
|
|
|
// Get the date from the row using dateKey
|
|
const rowDate = new Date(row[dateKey]);
|
|
|
|
if (rowDate < from || rowDate > to) {
|
|
isMatch = false;
|
|
}
|
|
}
|
|
|
|
if (isMatch) {
|
|
filteredRows.push(row);
|
|
}
|
|
}
|
|
|
|
setFilterRows(filteredRows);
|
|
|
|
// Determine if we are filtering or not
|
|
const isFiltering = Object.values(values).some(arr => arr.length > 0) ||
|
|
(fromDateQuery && toDateQuery);
|
|
|
|
setFiltered(isFiltering);
|
|
};
|
|
|
|
// Render date cell with formatting
|
|
const renderTableCell = (row, key) => {
|
|
const isDateField = key === 'createdat' || key === 'createdAt' ||
|
|
key === 'updated_at' || key === 'updatedAt' ||
|
|
key === 'created_at' || key === 'creat_at';
|
|
|
|
if (isDateField && row[key]) {
|
|
return formatDate(row[key]);
|
|
}
|
|
|
|
// Handle boolean values
|
|
if (typeof row[key] === 'boolean') {
|
|
return row[key] ? 'True' : 'False';
|
|
}
|
|
|
|
return row[key];
|
|
};
|
|
|
|
// Navigate back to the reports list
|
|
const goBack = () => {
|
|
navigate("/admin/report-runner");
|
|
};
|
|
|
|
// Add this function at an appropriate place in the component
|
|
const toggleExportDropdown = () => {
|
|
setShowExportDropdown(!showExportDropdown);
|
|
};
|
|
|
|
return (
|
|
<div className="container">
|
|
{/* Report Header */}
|
|
<h4 style={{ fontWeight: 300, display: "inline" }}>
|
|
<b>Report Name URL - {reportName}</b>
|
|
</h4>
|
|
<hr />
|
|
|
|
{/* Date and Standard Parameters Section */}
|
|
<div className="row">
|
|
{/* Date Parameters */}
|
|
{/* {dateParam && (
|
|
<div className="col-md-6">
|
|
<h5 style={{ fontWeight: 200, color: "black" }}>
|
|
<b>Date Range</b>
|
|
</h5>
|
|
<div className="form-group mb-3">
|
|
<label>Date Parameters</label>
|
|
<select
|
|
className="form-select"
|
|
value={selectDateType}
|
|
onChange={(e) => handleDateSelect(e.target.value)}
|
|
>
|
|
<option>--Select Particular--</option>
|
|
<option>Today</option>
|
|
<option>This Week</option>
|
|
<option>Last Week</option>
|
|
<option>This Month</option>
|
|
<option>Last Month</option>
|
|
<option>This Year</option>
|
|
<option>Last Year</option>
|
|
</select>
|
|
</div>
|
|
<div className="row">
|
|
<div className="col-md-6">
|
|
<label>From Date</label>
|
|
<input
|
|
type="date"
|
|
className="form-control"
|
|
value={fromDate}
|
|
onChange={(e) => {
|
|
setFromDate(e.target.value);
|
|
setFromDateQuery(e.target.value);
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className="col-md-6">
|
|
<label>To Date</label>
|
|
<input
|
|
type="date"
|
|
className="form-control"
|
|
value={toDate}
|
|
onChange={(e) => {
|
|
setToDate(e.target.value);
|
|
setToDateQuery(e.target.value);
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)} */}
|
|
|
|
{/* Standard Parameters */}
|
|
<div className="col-md-6">
|
|
<h5 style={{ fontWeight: 200, color: "black" }}>
|
|
<b>Standard Parameters</b>
|
|
</h5>
|
|
{dynamicFields.length === 0 ? (
|
|
<div className="text-danger">No parameter found</div>
|
|
) : (
|
|
<div className="row">
|
|
{dynamicFields.map((field, index) => (
|
|
<div key={index} className="col-md-6 mb-3">
|
|
<label>{field}</label>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
placeholder={`Enter ${field}`}
|
|
value={dynamicFormValues[field] || ''}
|
|
onChange={(e) => handleDynamicFormChange(field, e.target.value)}
|
|
/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Adhoc Parameters Section */}
|
|
<div className="row mt-4">
|
|
<div className="col-md-12">
|
|
<h5 style={{ fontWeight: 200, color: "black" }}>
|
|
<b>Adhoc Parameters</b>
|
|
</h5>
|
|
<table className="table">
|
|
<tbody>
|
|
{adhocParams.map((param, index) => (
|
|
<tr key={index}>
|
|
<td>
|
|
<select
|
|
className="form-select"
|
|
value={param.andor}
|
|
onChange={(e) =>
|
|
handleAdhocChange(index, "andor", e.target.value)
|
|
}
|
|
>
|
|
<option value="">Select Values</option>
|
|
{andOrOptions.map((option, idx) => (
|
|
<option key={idx} value={option}>
|
|
{option}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<select
|
|
className="form-select"
|
|
value={param.fields_name}
|
|
onChange={(e) =>
|
|
handleAdhocChange(index, "fields_name", e.target.value)
|
|
}
|
|
>
|
|
<option value="">Select Values</option>
|
|
{adhocList.map((field, idx) => (
|
|
<option key={idx} value={field}>
|
|
{field}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<select
|
|
className="form-select"
|
|
value={param.condition}
|
|
onChange={(e) =>
|
|
handleAdhocChange(index, "condition", e.target.value)
|
|
}
|
|
>
|
|
<option value="">Select Values</option>
|
|
{conditionOptions.map((condition, idx) => (
|
|
<option key={idx} value={condition}>
|
|
{condition}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
value={param.value}
|
|
onChange={(e) =>
|
|
handleAdhocChange(index, "value", e.target.value)
|
|
}
|
|
/>
|
|
</td>
|
|
<td>
|
|
<button
|
|
className="btn btn-danger me-2"
|
|
onClick={() => deleteAdhocRow(index)}
|
|
title="Delete Row"
|
|
>
|
|
<i className="bi bi-trash"></i>
|
|
</button>
|
|
<button
|
|
className="btn btn-primary"
|
|
onClick={addAdhocRow}
|
|
title="Add Row"
|
|
>
|
|
<i className="bi bi-plus"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Report Output Section */}
|
|
<div className="row mt-4">
|
|
<div className="col-md-6">
|
|
<h5 style={{ fontWeight: 300 }}>
|
|
<b>Report Output</b>
|
|
</h5>
|
|
</div>
|
|
<div className="col-md-6 text-end">
|
|
{/* Custom Export Dropdown */}
|
|
<div
|
|
id="export-dropdown-container"
|
|
className="d-inline-block me-2"
|
|
style={{ position: 'relative' }}
|
|
>
|
|
<button
|
|
type="button"
|
|
className="btn btn-outline-primary mr-3"
|
|
onClick={toggleExportDropdown}
|
|
>
|
|
<i className="bi bi-download me-1"></i> Export <i className="bi bi-caret-down-fill ms-1 small "></i>
|
|
</button>
|
|
{showExportDropdown && (
|
|
<div
|
|
className="shadow position-absolute end-0 bg-white border rounded mt-1 py-1"
|
|
style={{ zIndex: 1000, minWidth: '160px' }}
|
|
>
|
|
<button
|
|
type="button"
|
|
className="dropdown-item d-flex align-items-center px-3 py-2"
|
|
onClick={() => {
|
|
exportFile("xlsx");
|
|
setShowExportDropdown(false);
|
|
}}
|
|
>
|
|
<i className="bi bi-file-earmark-excel me-2 text-success"></i> XLSX
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className="dropdown-item d-flex align-items-center px-3 py-2"
|
|
onClick={() => {
|
|
exportFile("csv");
|
|
setShowExportDropdown(false);
|
|
}}
|
|
>
|
|
<i className="bi bi-file-earmark-text me-2 text-primary"></i> CSV
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className="dropdown-item d-flex align-items-center px-3 py-2"
|
|
onClick={() => {
|
|
exportFile("pdf");
|
|
setShowExportDropdown(false);
|
|
}}
|
|
>
|
|
<i className="bi bi-file-earmark-pdf me-2 text-danger"></i> PDF
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Back and Run Buttons */}
|
|
<button className="btn btn-outline-secondary me-2" onClick={goBack}>
|
|
Back
|
|
</button>
|
|
<button className="btn btn-primary" onClick={runQuery}>
|
|
Run
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<hr />
|
|
|
|
{/* Data Table */}
|
|
<div style={{ maxHeight: "500px", overflow: "auto", marginTop: "1rem" }}>
|
|
{isLoading ? (
|
|
<div className="text-center py-4">
|
|
<div className="spinner-border text-primary" role="status"></div>
|
|
<p className="mt-2">Loading data...</p>
|
|
</div>
|
|
) : error ? (
|
|
<div className="alert alert-danger">{error}</div>
|
|
) : (filtered ? filterRows : rows).length === 0 ? (
|
|
<div className="alert alert-info">No data available</div>
|
|
) : (
|
|
<table className="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
{(filtered ? getFilteredHeaders() : getHeaders()).map((header, index) => (
|
|
<th key={index}>{header}</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{(filtered ? filterRows : rows).map((row, rowIndex) => (
|
|
<tr key={rowIndex}>
|
|
{(filtered ? getFilteredHeaders() : getHeaders()).map((header, colIndex) => (
|
|
<td key={colIndex}>
|
|
{renderTableCell(row, header)}
|
|
</td>
|
|
))}
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ReportRunner2Edit;
|