This commit is contained in:
string 2025-06-16 09:52:39 +05:30
parent 5bc4dbf40d
commit 6fa8c7e7d3
21 changed files with 1610 additions and 777 deletions

39
package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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;

View 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;
}
};

View 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;
}
};

View 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;

View 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;
}
};

View 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;

View File

View 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
}
}

View 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
}
}

View 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
}
};

View 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;

View 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;
}
};

View 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,
};

View File

@ -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>

View File

@ -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

View File

@ -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/>}/>

View File

@ -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

View File

@ -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>
);
};