latest
This commit is contained in:
39
package-lock.json
generated
39
package-lock.json
generated
@@ -8,13 +8,14 @@
|
||||
"name": "log",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.11.3",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||
"@mui/icons-material": "^5.15.20",
|
||||
"@mui/material": "^5.15.20",
|
||||
"@material-ui/icons": "^4.11.3",
|
||||
"@mui/icons-material": "^5.17.1",
|
||||
"@mui/material": "^5.17.1",
|
||||
"@mui/styles": "^5.16.4",
|
||||
"@mui/x-charts": "^7.6.2",
|
||||
"@mui/x-data-grid": "^7.6.2",
|
||||
@@ -26,6 +27,7 @@
|
||||
"ajv-keywords": "^3.5.2",
|
||||
"axios": "^1.6.7",
|
||||
"chart.js": "^4.4.9",
|
||||
"file-saver": "^2.0.5",
|
||||
"mdb-react-ui-kit": "^7.1.0",
|
||||
"moment": "^2.30.1",
|
||||
"react": "^18.2.0",
|
||||
@@ -3764,6 +3766,29 @@
|
||||
"integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@material-ui/icons": {
|
||||
"version": "4.11.3",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.3.tgz",
|
||||
"integrity": "sha512-IKHlyx6LDh8n19vzwH5RtHIOHl9Tu90aAAxcbWME6kp4dmvODM3UvOHJeMIDzUbd4muuJKHmlNoBN+mDY4XkBA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.4.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@material-ui/core": "^4.0.0",
|
||||
"@types/react": "^16.8.6 || ^17.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/core-downloads-tracker": {
|
||||
"version": "5.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.17.1.tgz",
|
||||
@@ -9931,6 +9956,12 @@
|
||||
"webpack": "^4.0.0 || ^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/file-saver": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
|
||||
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/filelist": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||
|
||||
10
package.json
10
package.json
@@ -3,13 +3,14 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.11.3",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||
"@mui/icons-material": "^5.15.20",
|
||||
"@mui/material": "^5.15.20",
|
||||
"@material-ui/icons": "^4.11.3",
|
||||
"@mui/icons-material": "^5.17.1",
|
||||
"@mui/material": "^5.17.1",
|
||||
"@mui/styles": "^5.16.4",
|
||||
"@mui/x-charts": "^7.6.2",
|
||||
"@mui/x-data-grid": "^7.6.2",
|
||||
@@ -21,6 +22,7 @@
|
||||
"ajv-keywords": "^3.5.2",
|
||||
"axios": "^1.6.7",
|
||||
"chart.js": "^4.4.9",
|
||||
"file-saver": "^2.0.5",
|
||||
"mdb-react-ui-kit": "^7.1.0",
|
||||
"moment": "^2.30.1",
|
||||
"react": "^18.2.0",
|
||||
|
||||
@@ -1,117 +1,160 @@
|
||||
// import axios from 'axios';
|
||||
|
||||
// // Use the same environment variable name consistently
|
||||
// const BASE_URL = process.env.REACT_APP_API_BASE_URL;
|
||||
// console.log('API Base URL:', BASE_URL);
|
||||
// // Enhanced token service
|
||||
// export const getToken = () => {
|
||||
// // Check multiple possible storage locations
|
||||
// return localStorage.getItem('token') ||
|
||||
// sessionStorage.getItem('token') ||
|
||||
// document.cookie.split('; ').find(row => row.startsWith('token='))?.split('=')[1];
|
||||
// };
|
||||
|
||||
// const apiClient = axios.create({
|
||||
// baseURL: BASE_URL,
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/json',
|
||||
// },
|
||||
// });
|
||||
|
||||
// // Enhanced request interceptor
|
||||
// apiClient.interceptors.request.use(
|
||||
// (config) => {
|
||||
// const token = getToken();
|
||||
// if (token) {
|
||||
// config.headers['Authorization'] = `Bearer ${token}`;
|
||||
// // Add debug logging
|
||||
// console.debug('[API] Request with token:', {
|
||||
// url: config.url,
|
||||
// headers: config.headers
|
||||
// });
|
||||
// } else {
|
||||
// console.warn('[API] No token available for request:', config.url);
|
||||
// }
|
||||
// return config;
|
||||
// },
|
||||
// (error) => {
|
||||
// console.error('[API] Request interceptor error:', error);
|
||||
// return Promise.reject(error);
|
||||
// }
|
||||
// );
|
||||
|
||||
// // Enhanced response interceptor
|
||||
// apiClient.interceptors.response.use(
|
||||
// (response) => {
|
||||
// console.debug('[API] Successful response:', {
|
||||
// url: response.config.url,
|
||||
// status: response.status,
|
||||
// data: response.data
|
||||
// });
|
||||
// return response;
|
||||
// },
|
||||
// (error) => {
|
||||
// const originalRequest = error.config;
|
||||
|
||||
// // Debug logging
|
||||
// console.error('[API] Error response:', {
|
||||
// url: originalRequest.url,
|
||||
// status: error.response?.status,
|
||||
// data: error.response?.data
|
||||
// });
|
||||
|
||||
// // Handle 401 specifically
|
||||
// if (error.response?.status === 401 && !originalRequest._retry) {
|
||||
// originalRequest._retry = true;
|
||||
// console.warn('[API] Attempting token refresh...');
|
||||
// // Add your token refresh logic here if needed
|
||||
// }
|
||||
|
||||
// return Promise.reject(error);
|
||||
// }
|
||||
// );
|
||||
|
||||
// // API methods
|
||||
// const apiService = {
|
||||
// get: (url, params, options = {}) =>
|
||||
// apiClient.get(url, { ...options, params }).catch(handleError),
|
||||
|
||||
// post: (url, body = {}, options = {}) =>
|
||||
// apiClient.post(url, body, options).catch(handleError),
|
||||
|
||||
// put: (url, body = {}, options = {}) =>
|
||||
// apiClient.put(url, body, options).catch(handleError),
|
||||
|
||||
// delete: (url, options = {}) =>
|
||||
// apiClient.delete(url, options).catch(handleError),
|
||||
|
||||
// // Add patch if needed
|
||||
// patch: (url, body = {}, options = {}) =>
|
||||
// apiClient.patch(url, body, options).catch(handleError)
|
||||
// };
|
||||
|
||||
// // Enhanced error handler
|
||||
// const handleError = (error) => {
|
||||
// // Your existing error handling logic
|
||||
// // ...
|
||||
// };
|
||||
|
||||
// // Specific API endpoints
|
||||
// export const getSubmenuItems = async (id) => {
|
||||
// try {
|
||||
// const response = await apiService.get(`/api1/submenu1/${id}`);
|
||||
// return response.data;
|
||||
// } catch (error) {
|
||||
// console.error('Error fetching submenu items:', error);
|
||||
// throw error; // Re-throw for component-level handling
|
||||
// }
|
||||
// };
|
||||
|
||||
// // Export the configured service
|
||||
// export default apiService;
|
||||
|
||||
import axios from 'axios';
|
||||
import { getToken } from '../../src/utils/tokenService';
|
||||
|
||||
const BASE_URL = process.env.REACT_APP_API_BASE_URL;
|
||||
const BASE_URL = process.env.REACT_APP_API_BASE_URL ;
|
||||
|
||||
const apiClient = axios.create({
|
||||
const apiService = axios.create({
|
||||
baseURL: BASE_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
timeout: 30000,
|
||||
});
|
||||
// Add a request interceptor to include Authorization header
|
||||
apiClient.interceptors.request.use(
|
||||
|
||||
// Request interceptor for auth token
|
||||
apiService.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = getToken();
|
||||
const token = localStorage.getItem('authToken');
|
||||
if (token) {
|
||||
console.log("token: ",token);
|
||||
config.headers['Authorization'] = `Bearer ${token}`;
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// Generic error handler function
|
||||
const handleError = (error) => {
|
||||
let errorMessage = 'An unknown error occurred';
|
||||
console.error('Error Details:', error);
|
||||
|
||||
if (error.response) {
|
||||
// HTTP errors
|
||||
switch (error.response.status) {
|
||||
case 401:
|
||||
errorMessage = 'Unauthorized - Please login again.';
|
||||
break;
|
||||
case 403:
|
||||
errorMessage = 'Forbidden - Access denied.';
|
||||
break;
|
||||
case 404:
|
||||
errorMessage = 'Service not found.';
|
||||
break;
|
||||
case 408:
|
||||
errorMessage = 'Request timed out.';
|
||||
break;
|
||||
case 500:
|
||||
errorMessage = 'Internal server error.';
|
||||
break;
|
||||
default:
|
||||
errorMessage = `Unexpected error: ${error.response.statusText || 'Server Error'}`;
|
||||
// Response interceptor
|
||||
apiService.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response?.status === 401) {
|
||||
localStorage.removeItem('authToken');
|
||||
window.location.href = '/login';
|
||||
}
|
||||
} else if (error.request) {
|
||||
// No response received
|
||||
errorMessage = 'No response from server. Please check your connection.';
|
||||
} else {
|
||||
// Other errors
|
||||
errorMessage = error.message || 'An unexpected error occurred.';
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
return Promise.reject(errorMessage); // Return error message as rejected promise
|
||||
};
|
||||
|
||||
// Define the reusable methods
|
||||
// const apiService = {
|
||||
// get: (url, params) =>
|
||||
// apiClient
|
||||
// .get(url, { params: params || {} })
|
||||
// .catch(handleError), // Attach error handler
|
||||
// post: (url, body = {}) =>
|
||||
// apiClient
|
||||
// .post(url, body)
|
||||
// .catch(handleError), // Attach error handler
|
||||
// put: (url, body = {}) =>
|
||||
// apiClient
|
||||
// .put(url, body)
|
||||
// .catch(handleError), // Attach error handler
|
||||
// delete: (url) =>
|
||||
// apiClient
|
||||
// .delete(url)
|
||||
// .catch(handleError), // Attach error handler
|
||||
// };
|
||||
|
||||
const apiService = {
|
||||
get: (url, params) =>
|
||||
apiClient
|
||||
.get(url, { params: params || {} })
|
||||
.catch(handleError), // Attach error handler
|
||||
|
||||
post: (url, body = {}, options = {}) =>
|
||||
apiClient
|
||||
.post(url, body, options) // Pass options such as headers
|
||||
.catch(handleError), // Attach error handler
|
||||
|
||||
put: (url, body = {}, options = {}) =>
|
||||
apiClient
|
||||
.put(url, body, options) // Pass options such as headers
|
||||
.catch(handleError), // Attach error handler
|
||||
|
||||
delete: (url, options = {}) =>
|
||||
apiClient
|
||||
.delete(url, options) // Pass options such as headers
|
||||
.catch(handleError), // Attach error handler
|
||||
};
|
||||
// Add at the bottom of APIService.js
|
||||
|
||||
export const getSubmenuItems = async (id) => {
|
||||
const response = await apiService.get(`/api1/submenu1/${id}`);
|
||||
// Add downloadFile method
|
||||
apiService.downloadFile = async (url, data) => {
|
||||
try {
|
||||
const response = await apiService.post(url, data, {
|
||||
responseType: 'blob',
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
export const addSubmenuItem = async (menuId, submenuData) => {
|
||||
const response = await apiService.post(`/api1/menu/${menuId}/submenu`, submenuData);
|
||||
return response.data;
|
||||
};
|
||||
export const updateMenuItem = (id, formData) => apiService.put(`/api1/submenu1/${id}`, formData);
|
||||
export const deleteMenuItem = (id) => apiService.delete(`/api1/menu/${id}`);
|
||||
|
||||
|
||||
export default apiService;
|
||||
22
src/ApiServices/AddReportSQLBuilderAPI.js
Normal file
22
src/ApiServices/AddReportSQLBuilderAPI.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import axios from 'axios';
|
||||
|
||||
export const addSQLReport = async (formData) => {
|
||||
try {
|
||||
const token = localStorage.getItem('authToken');
|
||||
const response = await axios.post(
|
||||
`${process.env.REACT_APP_API_BASE_URL}/Rpt_builder2/Rpt_builder2`,
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error adding SQL report:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
17
src/ApiServices/DeleteReportSQLAPI.js
Normal file
17
src/ApiServices/DeleteReportSQLAPI.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import apiService from '../APIRequestService/APIServices';
|
||||
|
||||
|
||||
export const deleteReportSQL = async (id) => {
|
||||
if (!id) {
|
||||
throw new Error("ID is required for deletion.");
|
||||
}
|
||||
const BASE_URL = `${process.env.REACT_APP_API_BASE_URL}/Rpt_builder2/Rpt_builder2/${id}`;
|
||||
try {
|
||||
const response = await apiService.delete(BASE_URL);
|
||||
console.log("Delete response:", response.data);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error("Error in API call:", error.response || error.message);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
134
src/ApiServices/ReportBuilderService.js
Normal file
134
src/ApiServices/ReportBuilderService.js
Normal file
@@ -0,0 +1,134 @@
|
||||
import apiService from '../APIRequestService/APIServices';
|
||||
|
||||
const API_BASE_URL = 'api';
|
||||
|
||||
const ReportBuilderService = {
|
||||
getById: (id) => apiService.get(`${API_BASE_URL}/edit-report/${id}`),
|
||||
|
||||
buildReport: (id) => {
|
||||
const params = { id };
|
||||
return apiService.get(`${API_BASE_URL}/build_report`, params);
|
||||
},
|
||||
|
||||
getAll: (moduleId, page = 0, size = 1000) => {
|
||||
const params = { page, size, moduleId };
|
||||
return apiService.get(`${API_BASE_URL}/report-builder-by-id`, params);
|
||||
},
|
||||
|
||||
create: (fbHeader, moduleId) => {
|
||||
const params = { moduleId };
|
||||
return apiService.post(`${API_BASE_URL}/report-builder`, fbHeader, { params });
|
||||
},
|
||||
|
||||
createservicereport: (fbHeader, moduleId) => {
|
||||
const params = { moduleId };
|
||||
return apiService.post(`${API_BASE_URL}/report-builder_service`, fbHeader, { params });
|
||||
},
|
||||
|
||||
createQuery: (reportId) => {
|
||||
const params = { reportId };
|
||||
return apiService.post(`${API_BASE_URL}/add-master-query`, {}, { params });
|
||||
},
|
||||
|
||||
update: (id, functionRegister) => {
|
||||
return apiService.put(`${API_BASE_URL}/add-master-query/${id}`, functionRegister);
|
||||
},
|
||||
|
||||
updateservicereport: (id, functionRegister) => {
|
||||
return apiService.put(`${API_BASE_URL}/updatereport/${id}`, functionRegister);
|
||||
},
|
||||
|
||||
getMasterQuery: (id) => apiService.get(`${API_BASE_URL}/master-query/${id}`),
|
||||
|
||||
getMasterData: (query) => {
|
||||
const params = { sql_query: query };
|
||||
return apiService.get(`${API_BASE_URL}/master-query-data`, params);
|
||||
},
|
||||
|
||||
report2: (serviceName) => apiService.post(`${API_BASE_URL}/add-report`, serviceName),
|
||||
|
||||
saveq: (data) => apiService.post('FndQuery/query', data),
|
||||
|
||||
getall: () => apiService.get('FndQuery/query'),
|
||||
|
||||
getreportdata: (apiName) => {
|
||||
const url = `${API_BASE_URL}/${apiName}`;
|
||||
return apiService.get(url);
|
||||
},
|
||||
|
||||
getDatabase: () => apiService.get('SqlworkbenchSqlcont/sql'),
|
||||
|
||||
getTableListn: (val) => apiService.get(`Table_list/${val}`), // table list
|
||||
|
||||
getcolListn: (val, val1) => apiService.get(`Table_list/${val}/${val1}`), // column list
|
||||
|
||||
getColumnList: (tableSchema, tables) => {
|
||||
const params = { str: tables.join(',') };
|
||||
return apiService.get(`${API_BASE_URL}/AllTable_list/${tableSchema}`, { params });
|
||||
},
|
||||
|
||||
getAllColumnsFromAllTables: (tableNames) => {
|
||||
// If tableNames is an array, join it with commas
|
||||
const tables = Array.isArray(tableNames) ? tableNames.join(',') : tableNames;
|
||||
return apiService.get(`Alias_Table_list/${tables}`);
|
||||
},
|
||||
|
||||
getcollist: (table) => apiService.get(`fndMenu/loadcolumn/${table}`),
|
||||
|
||||
createdb: (data) => apiService.post('SqlworkbenchSqlcont/sql', data),
|
||||
|
||||
updateSqlModel: (id, sqlModel) => apiService.put(`SqlworkbenchSqlcont/sql/${id}`, sqlModel),
|
||||
|
||||
getSqlModelById: (id) => apiService.get(`SqlworkbenchSqlcont/sql/${id}`),
|
||||
|
||||
deleteSqlModel: (id) => apiService.delete(`SqlworkbenchSqlcont/sql/${id}`),
|
||||
|
||||
getallentity: () => apiService.get(`${API_BASE_URL}/report-builder`),
|
||||
|
||||
saveData: (data) => apiService.post('Rpt_builder/Rpt_builder', data),
|
||||
|
||||
getDetails: () => apiService.get('Rpt_builder/Rpt_builder'),
|
||||
|
||||
getDetailsById: (id) => apiService.get(`Rpt_builder/Rpt_builder/${id}`),
|
||||
|
||||
deleteById: (id) => apiService.delete(`Rpt_builder/Rpt_builder/${id}`),
|
||||
|
||||
updateData: (data, id) => apiService.put(`Rpt_builder/Rpt_builder/${id}`, data),
|
||||
|
||||
saverbData: (data) => apiService.post('Rpt_builder2/Rpt_builder2', data),
|
||||
|
||||
getrbDetails: () => apiService.get('Rpt_builder2/Rpt_builder2'),
|
||||
|
||||
getrbDetailsById: (id) => apiService.get(`Rpt_builder2/Rpt_builder2/${id}`),
|
||||
|
||||
deletrbById: (id) => apiService.delete(`Rpt_builder2/Rpt_builder2/${id}`),
|
||||
|
||||
updaterbData: (data, id) => apiService.put(`Rpt_builder2/Rpt_builder2/${id}`, data),
|
||||
|
||||
updaterbLineData: (data, id) => apiService.put(`Rpt_builder2_lines/update/${id}`, data),
|
||||
|
||||
getrbLineDetailsById: (id) => apiService.get(`Rpt_builder2_lines/Rpt_builder2_lines/${id}`),
|
||||
|
||||
getStdParamById: (id) => apiService.get(`Rpt_builder2/html/build_report2/${id}`),
|
||||
|
||||
getcolumnDetailsByurl: (url) => apiService.get(`Rpt_builder2_lines/geturlkeybyurl?url=${url}`),
|
||||
|
||||
getAllDetailsByurl: (url) => apiService.get(`Rpt_builder2_lines/fetch_data_url?url=${url}`),
|
||||
|
||||
downloadFile: async (format, dataList, name) => {
|
||||
const url = `${API_BASE_URL}/rbbuilder/fileconverter/downloadFile/${format}`;
|
||||
try {
|
||||
const fileData = await apiService.post(url, dataList, { responseType: 'blob' });
|
||||
const blob = new Blob([fileData], { type: 'application/octet-stream' });
|
||||
const fileName = name ? `${name}.${format}` : `download.${format}`;
|
||||
const link = document.createElement('a');
|
||||
link.href = window.URL.createObjectURL(blob);
|
||||
link.download = fileName;
|
||||
link.click();
|
||||
} catch (error) {
|
||||
console.error('Error downloading file:', error);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default ReportBuilderService;
|
||||
49
src/ApiServices/ReportRunnerAPI.js
Normal file
49
src/ApiServices/ReportRunnerAPI.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import apiService from '../APIRequestService/APIServices';
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
// Function to run a specific report
|
||||
export const runReport = async (reportId) => {
|
||||
try {
|
||||
const response = await apiService.get(`Rpt_builder2/Rpt_builder2/${reportId}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error("Error running report:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// Function to fetch all reports
|
||||
export const fetchAllReportsApi = async () => {
|
||||
try {
|
||||
const response = await apiService.get('Rpt_builder2/Rpt_builder2');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error("Error fetching reports:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// Function to fetch standard parameters
|
||||
export const fetchStandardParameters = async (url) => {
|
||||
try {
|
||||
const response = await apiService.get(`Rpt_builder2_lines/fetch_data_url?url=${encodeURIComponent(url)}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error("Error fetching parameters:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// File download function
|
||||
export const downloadFile = async (format, dataList, name) => {
|
||||
try {
|
||||
const blob = await apiService.downloadFile(
|
||||
`rbbuilder/fileconverter/downloadFile/${format}`,
|
||||
dataList
|
||||
);
|
||||
saveAs(blob, `${name}.${format}`);
|
||||
} catch (error) {
|
||||
console.error('Error downloading file:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
21
src/ApiServices/ReportSQLBuilderAPI.js
Normal file
21
src/ApiServices/ReportSQLBuilderAPI.js
Normal file
@@ -0,0 +1,21 @@
|
||||
// src/api/ReportSqlBuilderApi.js
|
||||
|
||||
import apiService from '../APIRequestService/APIServices';// Assuming apiService is in the same directory
|
||||
|
||||
|
||||
|
||||
const BASE_URL = `${process.env.REACT_APP_API_BASE_URL}/Rpt_builder2/Rpt_builder2`;
|
||||
console.log("BASE_URL:", BASE_URL); // Log the base URL for debugging
|
||||
const ReportSqlBuilderApi = {
|
||||
fetchUserDetails: async () => {
|
||||
try {
|
||||
const response = await apiService.get(BASE_URL);
|
||||
return response.data; // Return only the data part of the response
|
||||
} catch (error) {
|
||||
throw error; // Let the error be handled by the calling component
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default ReportSqlBuilderApi;
|
||||
|
||||
0
src/ApiServices/SQLWorksheetAPI.js
Normal file
0
src/ApiServices/SQLWorksheetAPI.js
Normal file
23
src/ApiServices/SystemparameterApi.js
Normal file
23
src/ApiServices/SystemparameterApi.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import apiService from '../APIRequestService/APIService';
|
||||
|
||||
export const getSysParameter = async (id) => {
|
||||
try {
|
||||
const response = await apiService.get(`/sysparam/getSysParams/${id}`);
|
||||
return response.data; // Return only the data part of the response
|
||||
} catch (error) {
|
||||
throw error; // Let the error be handled by the calling component
|
||||
}
|
||||
}
|
||||
|
||||
export const addSysParameter = async (formdata) => {
|
||||
try {
|
||||
const response = await apiService.put(`/sysparam/updateSysParams/${1}`,formdata);
|
||||
console.log("add response data ",response.data)
|
||||
return response.data; // Return only the data part of the response
|
||||
} catch (error) {
|
||||
throw error; // Let the error be handled by the calling component
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
16
src/ApiServices/UpdateReportSQLBuilderAPI.js
Normal file
16
src/ApiServices/UpdateReportSQLBuilderAPI.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import apiService from '../APIRequestService/APIServices';
|
||||
|
||||
|
||||
|
||||
|
||||
export const UpdateReportSQLBuilder = async (id,formData) => {
|
||||
const BASE_URL = `${process.env.REACT_APP_API_BASE_URL}/Rpt_builder2/Rpt_builder2/${id}`;
|
||||
try {
|
||||
const response = await apiService.put(BASE_URL, formData);
|
||||
console.log("update response: ",response.data);
|
||||
|
||||
return response.data; // Return only the data part of the response
|
||||
} catch (error) {
|
||||
throw error; // Let the error be handled by the calling component
|
||||
}
|
||||
}
|
||||
24
src/ApiServices/reportBaseUrlAddAPI.js
Normal file
24
src/ApiServices/reportBaseUrlAddAPI.js
Normal file
@@ -0,0 +1,24 @@
|
||||
// reportBaseUrlAddAPI.js
|
||||
import apiService from '../APIRequestService/APIServices';
|
||||
|
||||
// Function to handle the Add API request
|
||||
export const addReport = async (formData) => {
|
||||
try {
|
||||
const response = await apiService.post('/Rpt_builder2/Rpt_builder2', formData);
|
||||
|
||||
console.log("add Report response: ",response)
|
||||
return response; // Return the response to handle success in the component
|
||||
} catch (error) {
|
||||
throw error; // Re-throw error to handle it in the calling component
|
||||
}
|
||||
};
|
||||
|
||||
// Function to fetch keys from URL
|
||||
export const getKeysFromUrl = async (url) => {
|
||||
try {
|
||||
const response = await apiService.get(`/api/report-builder/columns`, { url });
|
||||
return response; // Return the response for further processing
|
||||
} catch (error) {
|
||||
throw error; // Re-throw error to handle it in the calling component
|
||||
}
|
||||
};
|
||||
35
src/ApiServices/reportBaseUrlAllAPI.js
Normal file
35
src/ApiServices/reportBaseUrlAllAPI.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import axios from 'axios';
|
||||
|
||||
// Create axios instance
|
||||
const apiService = axios.create({
|
||||
baseURL: process.env.REACT_APP_API_BASE_URL,
|
||||
});
|
||||
|
||||
// Add request interceptor to inject the token
|
||||
apiService.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = localStorage.getItem('authToken');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// Add response interceptor to handle 401 errors
|
||||
apiService.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response?.status === 401) {
|
||||
// Handle token expiration (e.g., redirect to login)
|
||||
localStorage.removeItem('authToken');
|
||||
window.location.href = '/login';
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default apiService;
|
||||
12
src/ApiServices/reportBaseUrlDeleteAPI.js
Normal file
12
src/ApiServices/reportBaseUrlDeleteAPI.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import apiService from '../APIRequestService/APIServices';
|
||||
|
||||
|
||||
export const deleteReport = async (reportId) => {
|
||||
try {
|
||||
const response = await apiService.delete(`/Rpt_builder2/Rpt_builder2/${reportId}`);
|
||||
console.log(response.data);
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
16
src/ApiServices/reportBaseUrlEditAPI.js
Normal file
16
src/ApiServices/reportBaseUrlEditAPI.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import apiService from '../APIRequestService/APIServices';
|
||||
|
||||
const editReport = async (id, payload) => {
|
||||
try {
|
||||
const response = await apiService.put(`Rpt_builder2_lines/update/${id}`, payload);
|
||||
console.log("Edit api report base url ", response.data)
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error("Error in editReport:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
editReport,
|
||||
};
|
||||
26
src/App.js
26
src/App.js
@@ -37,6 +37,8 @@ import DashboardNewAdd from "./components/dashboardnew/dashboardadd/dashboardbui
|
||||
import DashboardNewEdit from "./components/dashboardnew/editdashboard/editformdashboard";
|
||||
import EditNewDash from "./components/dashboardnew/editdashboard/editdashboard";
|
||||
import SubMenuMaintenance from "./components/Dashboard/sub menu/submenumaintanence";
|
||||
import ReportRunnerAll from "./components/Dashboard/reports/reportrunnerall";
|
||||
import ReportBuilderSQL from "./components/Dashboard/reports/reportbuildersql";
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
primary: {
|
||||
@@ -67,10 +69,10 @@ const App = () => {
|
||||
/>
|
||||
|
||||
<Routes>
|
||||
<Route path="/" element={<Login />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
|
||||
{/* Main dashboard route */}
|
||||
<Route path="/dashboard" element={<Dashboard />}>
|
||||
<Route path="/dashboard/*" element={<Dashboard />}>
|
||||
<Route index element={<HomePage />} />
|
||||
|
||||
{/* Setup section with all maintenance routes */}
|
||||
@@ -79,7 +81,7 @@ const App = () => {
|
||||
<Route path="user-maintenance" element={<UserMaintenance />} />
|
||||
<Route path="user-group-maintenance" element={<UserGroupMaintenance />} />
|
||||
<Route path="menu-maintenance" element={<MenuMaintenance />} />
|
||||
<Route path="sub-menu-maintenance/:menuItemId" element={<SubMenuMaintenance />} />
|
||||
<Route path="sub-menu-maintenance/:menuItemId" element={<SubMenuMaintenance/>} />
|
||||
|
||||
<Route path="menu-access-control" element={<MenuAccessControl />} />
|
||||
<Route path="system-parameters" element={<SystemParameters />} />
|
||||
@@ -92,21 +94,23 @@ const App = () => {
|
||||
<Route path="dynamic-table" element={<DynamicTable />} />
|
||||
|
||||
</Route>
|
||||
<Route path="dashboard-runner-all" element={<DashboardRunnerAll />} />
|
||||
<Route path="dashboard-new-all" element={<DashboardNewAll />} />
|
||||
<Route path="dashboard-new-add" element={<DashboardNewAdd />} />
|
||||
<Route path="dashboard-new-edit/:id" element={<DashboardNewEdit />} />
|
||||
<Route path="edit-new-dash/:id" element={<EditNewDash />} />
|
||||
<Route path="dashrunner/:id" element={<DashboardRunner />} />
|
||||
|
||||
<Route path="dashboard-runner-all" element={<DashboardRunnerAll/>}/>
|
||||
<Route path="dashboard-new-all" element={<DashboardNewAll/>}/>
|
||||
<Route path="dashboard-new-add" element={<DashboardNewAdd/>}/>
|
||||
<Route path="dashboard-new-edit/:id" element={<DashboardNewEdit/>}/>
|
||||
<Route path="edit-new-dash/:id" element={<EditNewDash/>}/>
|
||||
<Route path="dashrunner/:id" element={ <DashboardRunner/>}/>
|
||||
<Route path="reports" element={<Report />} />
|
||||
<Route path="user-report" element={<ReportBuilderSQL />} />
|
||||
{/* <Route path="reports" element={<ReportRunnerAll />} /> */}
|
||||
<Route path="about" element={<About />} />
|
||||
<Route path="profile" element={<Profile />} />
|
||||
</Route>
|
||||
|
||||
{/* buildercomponents */}
|
||||
|
||||
|
||||
|
||||
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getToken } from '../../../utils/tokenService';
|
||||
|
||||
// Create axios instance with base configuration
|
||||
const apiClient = axios.create({
|
||||
baseURL: process.env.REACT_APP_API_URL || 'http://157.66.191.31:33730/back/',
|
||||
baseURL: process.env.REACT_APP_API_URL ,
|
||||
});
|
||||
|
||||
// Add request interceptor to inject token
|
||||
|
||||
@@ -50,7 +50,7 @@ import ApiRegistery from "./ApiRegistery/ApiRegistery";
|
||||
import TokenRegistery from "./TokenRegistery/TokenRegistery";
|
||||
import HomePage from "./HomePage";
|
||||
import Setup from "./Setup";
|
||||
import Report from "./reports/Report";
|
||||
// import Report from "./reports/Report";
|
||||
import SequenceGenerator from "./document sequence/sequencegenerator";
|
||||
import About from "./dropdown/about";
|
||||
import Profile from "./dropdown/profile";
|
||||
@@ -61,7 +61,9 @@ import DashboardNewEdit from "../dashboardnew/editdashboard/editformdashboard";
|
||||
import EditNewDash from "../dashboardnew/editdashboard/editdashboard";
|
||||
import DashboardRunner from "../dashboardnew/dashboardrunner/dashboardrunner";
|
||||
import SubMenuMaintenance from "./sub menu/submenumaintanence";
|
||||
|
||||
import ReportRunnerAll from "./reports/reportrunnerall";
|
||||
import Report from "./reports/Report";
|
||||
import ReportBuilderSQL from "./reports/reportbuildersql";
|
||||
|
||||
const Dashboard = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -585,7 +587,8 @@ const Dashboard = () => {
|
||||
<Route path="document-sequence" element={<SequenceGenerator />} />
|
||||
<Route path="sub-menu-maintenance/:menuItemId" element={<SubMenuMaintenance/>} />
|
||||
</Route>
|
||||
<Route path="reports" element={<Report />} />
|
||||
<Route path="reports" element={<Report/>} />
|
||||
<Route path="user-report" element={<ReportBuilderSQL/>} />
|
||||
<Route path="about" element={<About/>} />
|
||||
<Route path="profile" element={<Profile/>} />
|
||||
<Route path="dashboard-runner-all" element={<DashboardRunnerAll/>}/>
|
||||
|
||||
@@ -1,32 +1,50 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
const Card = ({ report }) => {
|
||||
return (
|
||||
<div className="bg-white border border-gray-300 rounded-lg shadow-md p-6 m-4 cursor-pointer transition-transform transform hover:scale-105 hover:shadow-lg flex flex-col items-center justify-center text-center">
|
||||
<h2 className="text-lg font-semibold mb-2">{report.reportName}</h2>
|
||||
<p className="text-gray-600 mb-2">{report.description}</p>
|
||||
<p className="text-gray-600 mb-2">
|
||||
Active: {report.active ? "Yes" : "No"}
|
||||
</p>
|
||||
<p className="text-gray-600">Is SQL: {report.isSql ? "Yes" : "No"}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Define the API base URL using the environment variable
|
||||
const api = process.env.REACT_APP_API_BASE_URL;
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { toast } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import {
|
||||
Container,
|
||||
Grid,
|
||||
Card,
|
||||
CardHeader,
|
||||
CardContent,
|
||||
Typography,
|
||||
CircularProgress,
|
||||
Alert,
|
||||
Paper,
|
||||
Button,
|
||||
Avatar,
|
||||
Chip,
|
||||
Box,
|
||||
IconButton,
|
||||
Divider
|
||||
} from '@mui/material';
|
||||
import {
|
||||
AddCircleOutline,
|
||||
Storage as Database,
|
||||
Link as LinkIcon,
|
||||
CheckCircle,
|
||||
Cancel,
|
||||
MoreVert,
|
||||
AccessTime
|
||||
} from '@mui/icons-material';
|
||||
|
||||
const Report = () => {
|
||||
const [reports, setReports] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
const navigate = useNavigate();
|
||||
|
||||
const api = process.env.REACT_APP_API_BASE_URL;
|
||||
console.log("API Base URL:", api);
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
const token = localStorage.getItem("authToken");
|
||||
|
||||
if (!token) {
|
||||
console.error("No auth token found. Redirecting to login.");
|
||||
// You can redirect to the login page here if needed
|
||||
setError("No auth token found. Please login.");
|
||||
toast.error("Authentication required");
|
||||
navigate("/login");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -38,36 +56,275 @@ const Report = () => {
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch data");
|
||||
throw new Error(response.status === 401 ? "Session expired" : "Failed to fetch data");
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
setReports(data);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
setError(error.message);
|
||||
if (error.message === "Session expired") {
|
||||
toast.error("Session expired. Please login again.");
|
||||
navigate("/login");
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
}, [api, navigate]);
|
||||
|
||||
const goToAddSQLReport = () => {
|
||||
navigate("/dashboard/user-report");
|
||||
};
|
||||
|
||||
const goToAddURLReport = () => {
|
||||
navigate("/admin/reportbuild2all");
|
||||
};
|
||||
|
||||
const handleCardClick = (report) => {
|
||||
if (report.isSql) {
|
||||
navigate(`/admin/report-runner1/${report.id}`, {
|
||||
state: { reportId: report.id, reportData: report }
|
||||
});
|
||||
} else {
|
||||
navigate(`/admin/report-runner2/${report.id}`, {
|
||||
state: { reportId: report.id, reportData: report }
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-100 p-4 flex flex-col items-center">
|
||||
<div className="mb-6">
|
||||
<h1 className="text-3xl font-semibold text-gray-700">Reports</h1>
|
||||
</div>
|
||||
{loading ? (
|
||||
<p className="text-gray-700">Loading...</p>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
|
||||
{reports.map((report) => (
|
||||
<Card key={report.id} report={report} />
|
||||
))}
|
||||
</div>
|
||||
<Container maxWidth="xl" sx={{ py: 4 }}>
|
||||
{/* Header Section */}
|
||||
<Paper elevation={2} sx={{
|
||||
p: 3,
|
||||
mb: 4,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
borderRadius: '12px'
|
||||
}}>
|
||||
<Typography variant="h4" color="primary" sx={{ fontWeight: 'bold' }}>
|
||||
All Reports
|
||||
</Typography>
|
||||
<Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<AddCircleOutline />}
|
||||
onClick={goToAddSQLReport}
|
||||
sx={{ mr: 2 }}
|
||||
>
|
||||
Report Builder SQL
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<AddCircleOutline />}
|
||||
onClick={goToAddURLReport}
|
||||
>
|
||||
Report Builder URL
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
{/* Loading State */}
|
||||
{loading && (
|
||||
<Grid container justifyContent="center" sx={{ py: 8 }}>
|
||||
<CircularProgress size={60} />
|
||||
</Grid>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Error State */}
|
||||
{error && !loading && (
|
||||
<Alert severity="error" sx={{ mb: 4 }}>
|
||||
{error}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* Reports Grid */}
|
||||
{!loading && reports.length > 0 && (
|
||||
<Grid container spacing={3}>
|
||||
{reports.map((report) => (
|
||||
<Grid item xs={12} sm={6} md={4} lg={3} key={report.id}>
|
||||
<Card
|
||||
sx={{
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
cursor: 'pointer',
|
||||
transition: 'transform 0.3s, box-shadow 0.3s',
|
||||
'&:hover': {
|
||||
transform: 'translateY(-8px)',
|
||||
boxShadow: 6
|
||||
},
|
||||
borderRadius: '16px',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
onClick={() => handleCardClick(report)}
|
||||
>
|
||||
{/* Card Header with Gradient */}
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
borderBottom: '1px solid',
|
||||
borderColor: 'divider',
|
||||
background: report.isSql
|
||||
? 'linear-gradient(135deg, #f5f7fa 0%,rgb(198, 218, 255) 100%)'
|
||||
: 'linear-gradient(135deg, #fff9f0 0%,rgb(217, 204, 255) 100%)',
|
||||
height: '60px'
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Avatar
|
||||
sx={{
|
||||
width: 36,
|
||||
height: 36,
|
||||
mr: 2,
|
||||
borderRadius: '10px',
|
||||
bgcolor: report.isSql ? 'rgba(65, 105, 225, 0.15)' : 'rgba(255, 165, 0, 0.15)',
|
||||
color: report.isSql ? '#4169E1' : '#FF8C00'
|
||||
}}
|
||||
>
|
||||
{report.isSql ? <Database /> : <LinkIcon />}
|
||||
</Avatar>
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
sx={{
|
||||
color: report.isSql ? '#4169E1' : '#FF8C00',
|
||||
}}
|
||||
>
|
||||
{report.isSql ? "SQL Report" : "URL Report"}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Status Chip */}
|
||||
<Chip
|
||||
label={report.active ? "Active" : "Inactive"}
|
||||
size="small"
|
||||
color={report.active ? "success" : "error"}
|
||||
icon={report.active ? <CheckCircle fontSize="small" /> : <Cancel fontSize="small" />}
|
||||
sx={{
|
||||
backgroundColor: report.active ? 'rgba(40, 167, 69, 0.1)' : 'rgba(220, 53, 69, 0.1)',
|
||||
color: report.active ? '#28a745' : '#dc3545',
|
||||
px: 1,
|
||||
py: 0.5
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Card Content */}
|
||||
<CardContent sx={{ flexGrow: 1, p: 3 }}>
|
||||
<Typography
|
||||
gutterBottom
|
||||
variant="h6"
|
||||
component="div"
|
||||
noWrap
|
||||
sx={{
|
||||
fontWeight: 'bold',
|
||||
color: 'text.primary',
|
||||
mb: 2
|
||||
}}
|
||||
>
|
||||
{report.reportName}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="text.secondary"
|
||||
sx={{
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
overflow: 'hidden',
|
||||
minHeight: '40px',
|
||||
lineHeight: 1.5,
|
||||
fontSize: '0.9rem'
|
||||
}}
|
||||
>
|
||||
{report.description || "No description available"}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
|
||||
{/* Card Footer */}
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
borderTop: '1px solid',
|
||||
borderColor: 'divider',
|
||||
backgroundColor: 'background.default'
|
||||
}}
|
||||
>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<AccessTime fontSize="small" sx={{ mr: 1, color: 'text.secondary' }} />
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Updated: {new Date(report.updatedAt).toLocaleDateString()}
|
||||
</Typography>
|
||||
</Box>
|
||||
<IconButton
|
||||
size="small"
|
||||
sx={{
|
||||
backgroundColor: 'action.hover',
|
||||
'&:hover': {
|
||||
backgroundColor: 'action.selected'
|
||||
}
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
// Add menu functionality here
|
||||
}}
|
||||
>
|
||||
<MoreVert fontSize="small" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</Card>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{/* Empty State */}
|
||||
{!loading && reports.length === 0 && !error && (
|
||||
<Paper sx={{
|
||||
p: 4,
|
||||
textAlign: 'center',
|
||||
borderRadius: '12px'
|
||||
}}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
No reports available
|
||||
</Typography>
|
||||
<Typography variant="body1" color="text.secondary" sx={{ mb: 3 }}>
|
||||
Create your first report by clicking one of the "Add Report" buttons
|
||||
</Typography>
|
||||
<Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<AddCircleOutline />}
|
||||
onClick={goToAddSQLReport}
|
||||
sx={{ mr: 2 }}
|
||||
>
|
||||
Add SQL Report
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<AddCircleOutline />}
|
||||
onClick={goToAddURLReport}
|
||||
>
|
||||
Add URL Report
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,15 +2,45 @@ import React, { useState, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { toast } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import { fetchAllReportsApi } from '../../../APIServices/ReportRunnerAPI';
|
||||
import { fetchAllReportsApi } from '../../../ApiServices/ReportRunnerAPI';
|
||||
import {
|
||||
Container,
|
||||
Grid,
|
||||
Card,
|
||||
CardHeader,
|
||||
CardContent,
|
||||
CardActions,
|
||||
Typography,
|
||||
Button,
|
||||
CircularProgress,
|
||||
Alert,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Avatar,
|
||||
IconButton,
|
||||
Chip,
|
||||
Divider,
|
||||
Paper
|
||||
} from '@mui/material';
|
||||
import {
|
||||
AddCircleOutline,
|
||||
Link,
|
||||
Storage as Database,
|
||||
MoreVert,
|
||||
AccessTime,
|
||||
Delete,
|
||||
CheckCircle,
|
||||
Cancel
|
||||
} from '@mui/icons-material';
|
||||
|
||||
const ReportRunnerAll = () => {
|
||||
const [gridData, setGridData] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [rowSelected, setRowSelected] = useState(null);
|
||||
const [modalDelete, setModalDelete] = useState(false);
|
||||
const [reports, setReports] = useState([]);
|
||||
const [openDeleteDialog, setOpenDeleteDialog] = useState(false);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -23,16 +53,12 @@ const ReportRunnerAll = () => {
|
||||
try {
|
||||
const data = await fetchAllReportsApi();
|
||||
console.log("Fetched all reports:", data);
|
||||
|
||||
setGridData(data);
|
||||
setReports(data);
|
||||
|
||||
if (data.length === 0) {
|
||||
setError("No data available");
|
||||
}
|
||||
if (data.length === 0) setError("No data available");
|
||||
} catch (error) {
|
||||
console.error("Error while fetching reports:", error);
|
||||
setError("Error fetching data.");
|
||||
toast.error("Failed to load reports");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -42,170 +68,196 @@ const ReportRunnerAll = () => {
|
||||
const goToAdd2 = () => navigate("/admin/reportbuild2all");
|
||||
|
||||
const goToRunner = (report) => {
|
||||
console.log("at time of navigating reportID: ",report.id, " report: ",report);
|
||||
if (report.isSql) {
|
||||
navigate(`/admin/report-runner1/${report.id}`,{ state: { reportId: report.id, reportData: report}});
|
||||
navigate(`/admin/report-runner1/${report.id}`, {
|
||||
state: { reportId: report.id, reportData: report }
|
||||
});
|
||||
} else {
|
||||
navigate(`/admin/report-runner2/${report.id}`,{ state: { reportId: report.id, reportData: report}});
|
||||
navigate(`/admin/report-runner2/${report.id}`, {
|
||||
state: { reportId: report.id, reportData: report }
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
setModalDelete(false);
|
||||
setOpenDeleteDialog(false);
|
||||
try {
|
||||
const response = await fetch(`/api/report-builder/${id}`, { method: "DELETE" });
|
||||
const response = await fetch(`/api/report-builder/${id}`, {
|
||||
method: "DELETE"
|
||||
});
|
||||
if (response.ok) {
|
||||
toast.success("Deleted successfully");
|
||||
toast.success("Report deleted successfully");
|
||||
fetchAllReports();
|
||||
} else {
|
||||
toast.error("Error deleting data.");
|
||||
throw new Error("Failed to delete");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error("Error deleting data.");
|
||||
toast.error("Error deleting report");
|
||||
}
|
||||
};
|
||||
|
||||
const openDeleteModal = (row) => {
|
||||
setRowSelected(row);
|
||||
setModalDelete(true);
|
||||
setOpenDeleteDialog(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container-fluid p-4">
|
||||
<Container maxWidth="xl" sx={{ py: 4 }}>
|
||||
{/* Header */}
|
||||
<div className="d-flex justify-content-between align-items-center mb-4">
|
||||
<h3 className="m-0"><strong>All Reports</strong></h3>
|
||||
<Paper elevation={2} sx={{ p: 3, mb: 4, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Typography variant="h4" color="primary">
|
||||
<Database sx={{ verticalAlign: 'middle', mr: 1 }} />
|
||||
All Reports
|
||||
</Typography>
|
||||
<div>
|
||||
<button className="btn btn-primary me-2" onClick={goToAdd}>
|
||||
<i className="bi bi-plus me-1"></i> Report Builder SQL
|
||||
</button>
|
||||
<button className="btn btn-primary" onClick={goToAdd2}>
|
||||
<i className="bi bi-plus me-1"></i> Report Builder URL
|
||||
</button>
|
||||
</div>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<AddCircleOutline />}
|
||||
onClick={goToAdd}
|
||||
sx={{ mr: 2 }}
|
||||
>
|
||||
SQL Report
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Link />}
|
||||
onClick={goToAdd2}
|
||||
>
|
||||
URL Report
|
||||
</Button>
|
||||
</div>
|
||||
</Paper>
|
||||
|
||||
{/* Loading Spinner */}
|
||||
{/* Loading state */}
|
||||
{isLoading && (
|
||||
<div className="alert alert-info d-flex align-items-center mt-3">
|
||||
<div className="spinner-border me-2" role="status"></div>
|
||||
Loading...
|
||||
</div>
|
||||
<Grid container justifyContent="center" sx={{ py: 8 }}>
|
||||
<CircularProgress size={60} />
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{/* Error state */}
|
||||
{error && !isLoading && (
|
||||
<Alert severity="error" sx={{ mb: 4 }}>
|
||||
{error}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* Report Cards */}
|
||||
{!isLoading && gridData.length > 0 && (
|
||||
<div className="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 g-4">
|
||||
<Grid container spacing={3}>
|
||||
{gridData.map((report, index) => (
|
||||
<div className="col" key={index}>
|
||||
<div
|
||||
className="card h-100 shadow-sm border-0"
|
||||
<Grid item xs={12} sm={6} md={4} lg={3} key={index}>
|
||||
<Card
|
||||
sx={{
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
cursor: 'pointer',
|
||||
transition: 'transform 0.3s, box-shadow 0.3s',
|
||||
'&:hover': {
|
||||
transform: 'translateY(-8px)',
|
||||
boxShadow: 6
|
||||
}
|
||||
}}
|
||||
onClick={() => goToRunner(report)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
{/* Card Header */}
|
||||
<div
|
||||
className={`card-header d-flex justify-content-between align-items-center ${report.isSql ? 'bg-primary bg-opacity-10' : 'bg-warning bg-opacity-10'}`}
|
||||
>
|
||||
<div className="d-flex align-items-center">
|
||||
<div className={`me-2 p-2 rounded ${report.isSql ? 'bg-primary bg-opacity-25' : 'bg-warning bg-opacity-25'}`}>
|
||||
<i className={`bi ${report.isSql ? 'bi-database' : 'bi-link-45deg'} ${report.isSql ? 'text-primary' : 'text-warning'}`}></i>
|
||||
</div>
|
||||
<span className={`fw-semibold ${report.isSql ? 'text-primary' : 'text-warning'}`}>
|
||||
{report.isSql == null ? "N/A" : report.isSql ? "SQL Report" : "URL Report"}
|
||||
</span>
|
||||
</div>
|
||||
<span className={`badge ${report.active ? 'bg-success' : 'bg-danger'}`}>
|
||||
{report.active ? "Active" : "Inactive"}
|
||||
</span>
|
||||
</div>
|
||||
<CardHeader
|
||||
avatar={
|
||||
<Avatar sx={{
|
||||
bgcolor: report.isSql ? 'primary.light' : 'secondary.light',
|
||||
color: report.isSql ? 'primary.main' : 'secondary.main'
|
||||
}}>
|
||||
{report.isSql ? <Database /> : <Link />}
|
||||
</Avatar>
|
||||
}
|
||||
action={
|
||||
<Chip
|
||||
label={report.active ? "Active" : "Inactive"}
|
||||
size="small"
|
||||
color={report.active ? "success" : "error"}
|
||||
icon={report.active ? <CheckCircle fontSize="small" /> : <Cancel fontSize="small" />}
|
||||
sx={{ ml: 1 }}
|
||||
/>
|
||||
}
|
||||
title={
|
||||
<Typography variant="subtitle1" color="text.secondary">
|
||||
{report.isSql ? "SQL Report" : "URL Report"}
|
||||
</Typography>
|
||||
}
|
||||
sx={{
|
||||
bgcolor: report.isSql ? 'primary.50' : 'secondary.50',
|
||||
borderBottom: '1px solid',
|
||||
borderColor: 'divider'
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Card Body */}
|
||||
<div className="card-body">
|
||||
<h5 className="card-title fw-bold text-truncate" title={report.reportName}>
|
||||
{/* Card Content */}
|
||||
<CardContent sx={{ flexGrow: 1 }}>
|
||||
<Typography gutterBottom variant="h6" component="div" noWrap>
|
||||
{report.reportName}
|
||||
</h5>
|
||||
<p
|
||||
className="card-text text-muted"
|
||||
style={{
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: '2',
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
overflow: 'hidden',
|
||||
height: '42px'
|
||||
}}
|
||||
title={report.description}
|
||||
>
|
||||
minHeight: '40px'
|
||||
}}>
|
||||
{report.description || "No description available"}
|
||||
</p>
|
||||
</div>
|
||||
</Typography>
|
||||
</CardContent>
|
||||
|
||||
{/* Card Footer */}
|
||||
<div className="card-footer bg-light">
|
||||
<div className="d-flex justify-content-between align-items-center">
|
||||
<small className="text-muted">
|
||||
<i className="bi bi-clock me-1"></i>
|
||||
<Divider />
|
||||
<CardActions sx={{ justifyContent: 'space-between', p: 2 }}>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<AccessTime fontSize="small" sx={{ mr: 0.5 }} />
|
||||
Updated: {new Date(report.updatedAt).toLocaleDateString()}
|
||||
</small>
|
||||
<button
|
||||
className="btn btn-sm btn-outline-secondary"
|
||||
</Typography>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
openDeleteModal(report);
|
||||
}}
|
||||
>
|
||||
<i className="bi bi-three-dots"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MoreVert />
|
||||
</IconButton>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</Grid>
|
||||
))}
|
||||
</div>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{/* Error */}
|
||||
{error && <div className="alert alert-danger mt-3">{error}</div>}
|
||||
|
||||
{/* Delete Modal */}
|
||||
{modalDelete && (
|
||||
<div className="modal show d-block" tabIndex="-1" style={{ backgroundColor: 'rgba(0,0,0,0.5)' }}>
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">Delete Confirmation</h5>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
onClick={() => setModalDelete(false)}
|
||||
></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<p>Are you sure you want to delete the report?</p>
|
||||
<h6>{rowSelected?.reportName}</h6>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
onClick={() => setModalDelete(false)}
|
||||
{/* Delete Dialog */}
|
||||
<Dialog
|
||||
open={openDeleteDialog}
|
||||
onClose={() => setOpenDeleteDialog(false)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-danger"
|
||||
onClick={() => handleDelete(rowSelected.id)}
|
||||
<DialogTitle>Delete Confirmation</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography>
|
||||
Are you sure you want to delete the report?
|
||||
</Typography>
|
||||
<Typography variant="subtitle1" sx={{ mt: 1 }}>
|
||||
{rowSelected?.reportName}
|
||||
</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setOpenDeleteDialog(false)}>Cancel</Button>
|
||||
<Button
|
||||
onClick={() => handleDelete(rowSelected?.id)}
|
||||
color="error"
|
||||
startIcon={<Delete />}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user