Files
authsec_reactbootstrapnew/src/components/Dashboard/ReportRunner/ReportRunner2Edit.js
Harish Sargar 0e1281aaa8 first commit
2025-04-01 20:28:04 +05:30

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;