diff --git a/.env.local b/.env.local
index 27570e5..acf8695 100644
--- a/.env.local
+++ b/.env.local
@@ -1 +1,6 @@
-DATABASE_URL="postgres://neondb_owner:npg_fthjFP8Ece2Z@ep-icy-leaf-afdbv5ts-pooler.c-2.us-west-2.aws.neon.tech/neondb?sslmode=require"
+# .env
+DB_HOST=localhost
+DB_PORT=5432
+DB_NAME=databas # Name of your PostgreSQL database
+DB_USER=postgres # Your PostgreSQL username
+DB_PASSWORD=blackEARTH3018334
\ No newline at end of file
diff --git a/lib/db.js b/lib/db.js
index 0bf2a63..fcf643c 100644
--- a/lib/db.js
+++ b/lib/db.js
@@ -1,13 +1,20 @@
-import bcrypt from 'bcrypt';
-import pkg from 'pg';
-const { Pool } = pkg;
+const { Pool } = require('pg');
const pool = new Pool({
- connectionString: process.env.DATABASE_URL,
- ssl: {
- rejectUnauthorized: false,
- },
+ host: process.env.DB_HOST,
+ port: process.env.DB_PORT,
+ database: process.env.DB_NAME,
+ user: process.env.DB_USER,
+ password: process.env.DB_PASSWORD,
});
-export { bcrypt };
-export default pool;
+// Test the connection
+pool.query('SELECT NOW()', (err, res) => {
+ if (err) {
+ console.error('Database connection error:', err);
+ } else {
+ console.log('Database connected successfully');
+ }
+});
+
+module.exports = pool;
\ No newline at end of file
diff --git a/src/app/api/reports/insurance-analysis/route.js b/src/app/api/reports/insurance-analysis/route.js
index 15ed5ea..d37234a 100644
--- a/src/app/api/reports/insurance-analysis/route.js
+++ b/src/app/api/reports/insurance-analysis/route.js
@@ -1,147 +1,71 @@
-import pool from '../../../../../lib/db';
import { NextResponse } from 'next/server';
+import pool from '../../../../../lib/db';
export async function GET(request) {
try {
const { searchParams } = new URL(request.url);
const startDate = searchParams.get('startDate');
const endDate = searchParams.get('endDate');
- const analysisType = searchParams.get('type') || 'detailed'; // detailed or summary
- if (analysisType === 'summary') {
- // Monthly summary view
- let summaryQuery = `
+ let query = `
+ WITH monthly_data AS (
SELECT
- DATE_TRUNC('month', a.date) AS month_year,
- COUNT(DISTINCT a.appointment_id) AS total_appointments,
- SUM(COALESCE(pay.amount, 0)) AS total_payments,
- SUM(COALESCE(ic.amount_approved, 0)) AS total_insurance_covered,
- SUM(COALESCE(pay.amount, 0) - COALESCE(ic.amount_approved, 0)) AS total_out_of_pocket,
- CASE
- WHEN SUM(COALESCE(pay.amount, 0)) > 0 THEN
- (SUM(COALESCE(ic.amount_approved, 0)) / SUM(pay.amount) * 100)
- ELSE 0
- END AS overall_insurance_coverage_percentage,
- COUNT(DISTINCT CASE WHEN ic.amount_approved > 0 THEN a.appointment_id END) AS appointments_with_insurance,
- COUNT(DISTINCT CASE WHEN ic.amount_approved IS NULL OR ic.amount_approved = 0 THEN a.appointment_id END) AS appointments_without_insurance
+ DATE_TRUNC('month', a.date) as month_year,
+ COUNT(DISTINCT a.appointment_id) as total_appointments,
+ SUM(p.amount) as total_payments,
+ SUM(COALESCE(ic.amount_approved, 0)) as total_insurance_covered,
+ SUM(p.amount - COALESCE(ic.amount_approved, 0)) as total_out_of_pocket
FROM appointment a
- LEFT JOIN payment pay ON a.appointment_id = pay.appointment_id
- LEFT JOIN insurance_claim ic ON a.appointment_id = ic.appointment_id AND ic.status = 'Approved'
- WHERE a.date IS NOT NULL
- `;
-
- const queryParams = [];
- let paramIndex = 1;
-
- if (startDate) {
- summaryQuery += ` AND a.date >= $${paramIndex}`;
- queryParams.push(startDate);
- paramIndex++;
- }
-
- if (endDate) {
- summaryQuery += ` AND a.date <= $${paramIndex}`;
- queryParams.push(endDate);
- paramIndex++;
- }
-
- summaryQuery += `
- GROUP BY DATE_TRUNC('month', a.date)
- ORDER BY month_year DESC
- `;
-
- const summaryResult = await pool.query(summaryQuery, queryParams);
-
- return NextResponse.json({
- success: true,
- type: 'summary',
- data: summaryResult.rows,
- summary: {
- totalRecords: summaryResult.rows.length,
- dateRange: { start: startDate, end: endDate }
- }
- }, { status: 200 });
-
- } else {
- // Detailed view
- let detailedQuery = `
- SELECT
- a.appointment_id,
- p.patient_id,
- p.name AS patient_name,
- a.date AS appointment_date,
- COALESCE(pay.amount, 0) AS total_payment,
- COALESCE(ic.amount_approved, 0) AS insurance_covered,
- COALESCE(pay.amount, 0) - COALESCE(ic.amount_approved, 0) AS out_of_pocket,
- CASE
- WHEN COALESCE(pay.amount, 0) > 0 THEN
- (COALESCE(ic.amount_approved, 0) / pay.amount * 100)
- ELSE 0
- END AS insurance_coverage_percentage,
- ip.policy_name,
- ip.coverage_percentage AS policy_coverage_rate,
- ic.status AS claim_status,
- ic.amount_claimed,
- d.name AS doctor_name,
- b.name AS branch_name
- FROM appointment a
- INNER JOIN patient p ON a.patient_id = p.patient_id
- LEFT JOIN payment pay ON a.appointment_id = pay.appointment_id
+ INNER JOIN payment p ON a.appointment_id = p.appointment_id
LEFT JOIN insurance_claim ic ON a.appointment_id = ic.appointment_id
- LEFT JOIN insurance_policy ip ON p.insurance_id = ip.insurance_id
- LEFT JOIN doctor d ON a.doctor_id = d.doctor_id
- LEFT JOIN branch b ON a.branch_id = b.branch_id
- WHERE (pay.amount IS NOT NULL OR ic.amount_approved IS NOT NULL)
- `;
-
- const queryParams = [];
- let paramIndex = 1;
-
- if (startDate) {
- detailedQuery += ` AND a.date >= $${paramIndex}`;
- queryParams.push(startDate);
- paramIndex++;
- }
-
- if (endDate) {
- detailedQuery += ` AND a.date <= $${paramIndex}`;
- queryParams.push(endDate);
- paramIndex++;
- }
-
- detailedQuery += ` ORDER BY a.date DESC`;
-
- const detailedResult = await pool.query(detailedQuery, queryParams);
-
- // Calculate summary statistics
- const totalPayments = detailedResult.rows.reduce((sum, row) => sum + parseFloat(row.total_payment), 0);
- const totalInsuranceCovered = detailedResult.rows.reduce((sum, row) => sum + parseFloat(row.insurance_covered), 0);
- const totalOutOfPocket = detailedResult.rows.reduce((sum, row) => sum + parseFloat(row.out_of_pocket), 0);
- const appointmentsWithInsurance = detailedResult.rows.filter(row => parseFloat(row.insurance_covered) > 0).length;
+ WHERE a.status = 'Completed'
+ ${startDate ? "AND a.date >= $1" : ""}
+ ${endDate ? `AND a.date <= $${startDate ? "2" : "1"}` : ""}
+ GROUP BY DATE_TRUNC('month', a.date)
+ )
+ SELECT
+ to_char(month_year, 'YYYY-MM-DD') as month_year,
+ total_appointments,
+ total_payments::numeric(10,2),
+ total_insurance_covered::numeric(10,2),
+ total_out_of_pocket::numeric(10,2),
+ CASE
+ WHEN total_payments > 0 THEN
+ ROUND((total_insurance_covered / total_payments * 100)::numeric, 2)
+ ELSE 0
+ END as coverage_percentage
+ FROM monthly_data
+ ORDER BY month_year;
+ `;
+
+ const queryParams = [];
+ if (startDate) queryParams.push(startDate);
+ if (endDate) queryParams.push(endDate);
+
+ console.log('Executing query with params:', queryParams); // Debug log
+
+ const result = await pool.query(query, queryParams);
+
+ console.log('Query results:', result.rows); // Debug log
+
+ const summary = {
+ total_appointments: result.rows.reduce((sum, row) => sum + Number(row.total_appointments), 0),
+ total_payments: result.rows.reduce((sum, row) => sum + Number(row.total_payments), 0),
+ total_insurance_covered: result.rows.reduce((sum, row) => sum + Number(row.total_insurance_covered), 0),
+ total_out_of_pocket: result.rows.reduce((sum, row) => sum + Number(row.total_out_of_pocket), 0),
+ avg_coverage_percentage: result.rows.length > 0
+ ? result.rows.reduce((sum, row) => sum + Number(row.coverage_percentage), 0) / result.rows.length
+ : 0
+ };
- return NextResponse.json({
- success: true,
- type: 'detailed',
- data: detailedResult.rows,
- summary: {
- totalAppointments: detailedResult.rows.length,
- totalPayments: totalPayments,
- totalInsuranceCovered: totalInsuranceCovered,
- totalOutOfPocket: totalOutOfPocket,
- appointmentsWithInsurance: appointmentsWithInsurance,
- appointmentsWithoutInsurance: detailedResult.rows.length - appointmentsWithInsurance,
- overallInsuranceCoveragePercentage: totalPayments > 0 ? (totalInsuranceCovered / totalPayments * 100) : 0,
- dateRange: { start: startDate, end: endDate }
- }
- }, { status: 200 });
- }
+ return NextResponse.json({
+ success: true,
+ data: result.rows,
+ summary
+ });
} catch (error) {
- console.error('Insurance coverage analysis error:', error);
- return NextResponse.json({
- success: false,
- error: 'Failed to generate insurance coverage analysis',
- details: error.message
- }, { status: 500 });
+ console.error('Insurance analysis error:', error);
+ return NextResponse.json({ error: error.message }, { status: 500 });
}
}
\ No newline at end of file
diff --git a/src/app/reports/page.js b/src/app/reports/page.js
index 3a519af..bf5f485 100644
--- a/src/app/reports/page.js
+++ b/src/app/reports/page.js
@@ -1,696 +1,213 @@
'use client';
-import Sidebar from '../components/Sidebar';
-import { useEffect, useState } from 'react';
-import { BarChart, LineChart, PieChart } from '../components/Chart';
-export default function ReportsPage() {
+import { useState, useEffect } from 'react';
+import { Chart as ChartJS } from 'chart.js/auto';
+import { Bar } from 'react-chartjs-2';
+import dynamic from 'next/dynamic';
+
+// Dynamic imports to prevent hydration mismatch
+const Sidebar = dynamic(() => import('../components/Sidebar'), { ssr: false });
+const ReportsTopBar = dynamic(() => import('../components/ReportsTopBar'), { ssr: false });
+
+export default function InsuranceClaimsReport() {
const [collapsed, setCollapsed] = useState(false);
- const [activeReport, setActiveReport] = useState(null);
- const [reportData, setReportData] = useState(null);
- const [filters, setFilters] = useState({
- date: '',
- startDate: '',
- endDate: '',
- branchId: '',
- doctorId: '',
- });
- const [loading, setLoading] = useState(false);
+ const [data, setData] = useState([]);
+ const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
- const [userRole, setUserRole] = useState('');
+ const [dateRange, setDateRange] = useState({
+ startDate: '2025-01-01',
+ endDate: '2025-12-31'
+ });
useEffect(() => {
- if (typeof window !== 'undefined') {
- const role = localStorage.getItem('role');
- setUserRole(role || '');
-
- // Redirect receptionists away from reports
- if (role === 'reception') {
- window.location.href = '/dashboard';
- return;
- }
- }
- }, []);
+ fetchData();
+ }, [dateRange]);
- // Only show doctor revenue report for doctors
- const reports = userRole === 'doctor'
- ? [
- {
- id: 'doctor-revenue',
- title: 'Doctor Revenue Report',
- description: 'Revenue and patient counts by doctor',
- icon: '👨⚕️',
- endpoint: '/api/reports/doctor-revenue',
- type: 'date-range'
- }
- ]
- : [
- {
- id: 'branch-revenue',
- title: 'Branch Revenue Report',
- description: 'Revenue breakdown by branch',
- icon: '🏢',
- endpoint: '/api/reports/branch-revenue',
- type: 'date-range'
- },
- {
- id: 'doctor-revenue',
- title: 'Doctor Revenue Report',
- description: 'Revenue and patient counts by doctor',
- icon: '👨⚕️',
- endpoint: '/api/reports/doctor-revenue',
- type: 'date-range'
- },
- {
- id: 'insurance-claims',
- title: 'Insurance Analysis Report',
- description: 'Insurance coverage vs out-of-pocket analysis',
- icon: '🛡️',
- endpoint: '/api/reports/insurance-analysis',
- type: 'date-range'
- }
- ];
-
- // Fetch report data dynamically based on active report
- const fetchReport = async (reportId) => {
- if (!reportId) return;
-
- const selected = reports.find(r => r.id === reportId);
- if (!selected) return;
-
- setLoading(true);
- setError('');
- setReportData(null);
-
+ async function fetchData() {
try {
- let url = selected.endpoint;
- const params = new URLSearchParams();
-
- if (selected.type === 'date-range') {
- if (filters.startDate) params.append('startDate', filters.startDate);
- if (filters.endDate) params.append('endDate', filters.endDate);
- }
+ setLoading(true);
+ setError('');
+ const params = new URLSearchParams(dateRange);
+ const res = await fetch(`/api/reports/insurance-analysis?${params}`);
- // Special handling for insurance-analysis API
- if (selected.id === 'insurance-claims') {
- params.append('type', 'summary');
+ if (!res.ok) {
+ throw new Error(`HTTP error! status: ${res.status}`);
}
- if (filters.branchId) params.append('branchId', filters.branchId);
- if (filters.doctorId) params.append('doctorId', filters.doctorId);
+ const result = await res.json();
- if (params.toString()) {
- url += '?' + params.toString();
- }
-
- const res = await fetch(url);
- if (!res.ok) {
- throw new Error(`Failed to fetch ${selected.title}: ${res.status}`);
+ if (result.success && result.data) {
+ console.log('Fetched data:', result.data); // Debug log
+ setData(result.data);
+ } else {
+ throw new Error(result.error || 'Failed to load data');
}
-
- const data = await res.json();
- setReportData(data);
} catch (err) {
- console.error('Report fetch error:', err);
- setError(err.message);
- setReportData(null);
+ console.error('Fetch error:', err);
+ setError(err.message || 'Failed to fetch report data');
+ setData([]);
} finally {
setLoading(false);
}
+ }
+
+ const chartData = {
+ labels: data.map(item => new Date(item.month_year).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'short'
+ })),
+ datasets: [
+ {
+ label: 'Insurance Covered',
+ data: data.map(item => Number(item.total_insurance_covered || 0)),
+ backgroundColor: 'rgba(34,197,94,0.7)',
+ borderColor: 'rgba(34,197,94,1)',
+ borderWidth: 1,
+ stack: 'stack0'
+ },
+ {
+ label: 'Out of Pocket',
+ data: data.map(item => Number(item.total_out_of_pocket || 0)),
+ backgroundColor: 'rgba(239,68,68,0.7)',
+ borderColor: 'rgba(239,68,68,1)',
+ borderWidth: 1,
+ stack: 'stack0'
+ }
+ ]
};
- // Renders charts and tables for each report type
- const renderReportData = () => {
- if (!activeReport) {
- return (
-
-
-
Select a Report
-
Choose a report from the available options to view analytics and insights
-
- );
- }
-
- if (loading) {
- return (
-
-
-
Generating report...
-
- );
- }
-
- if (error) {
- return (
-
-
-
-
-
Report Error
-
{error}
-
-
-
- );
- }
-
- if (!reportData || !reportData.data || reportData.data.length === 0) {
- return (
-
-
-
No Data Available
-
There is no data to display for the selected report and filters.
-
- );
- }
-
- switch (activeReport) {
- case 'branch-revenue': {
- // reportData is an array directly from the API
- const data = Array.isArray(reportData) ? reportData : [];
-
- const chartData = {
- labels: data.map(b => b.branch_name || 'Unknown Branch'),
- datasets: [
- {
- label: 'Total Revenue (Rs.)',
- data: data.map(b => Number(b.total_revenue) || 0),
- backgroundColor: 'rgba(34,197,94,0.8)',
- borderColor: 'rgba(34,197,94,1)',
- borderWidth: 1
+ const chartOptions = {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {
+ legend: {
+ position: 'top',
+ labels: {
+ padding: 20,
+ font: { size: 12 }
+ }
+ },
+ title: {
+ display: true,
+ text: 'Insurance Coverage vs Out of Pocket Expenses',
+ font: { size: 16, weight: 'bold' },
+ padding: { bottom: 30 }
+ },
+ tooltip: {
+ callbacks: {
+ label: function(context) {
+ let label = context.dataset.label || '';
+ if (label) {
+ label += ': ';
}
- ]
- };
-
- const chartOptions = {
- responsive: true,
- plugins: {
- legend: { position: 'top' },
- title: { display: true, text: 'Revenue by Branch' }
- },
- scales: {
- y: { beginAtZero: true }
- }
- };
-
- const totalRevenue = data.reduce((sum, branch) => sum + (Number(branch.total_revenue) || 0), 0);
- const avgRevenue = data.length > 0 ? totalRevenue / data.length : 0;
-
- return (
-
- {/* Summary Cards */}
-
-
-
-
-
Total Revenue
-
Rs. {totalRevenue.toLocaleString()}
-
-
- 💰
-
-
-
-
-
-
-
-
Total Branches
-
{data.length}
-
-
- 🏢
-
-
-
-
-
-
-
-
Avg Revenue
-
Rs. {Math.round(avgRevenue).toLocaleString()}
-
-
- 📊
-
-
-
-
-
- {/* Chart */}
-
-
-
-
- {/* Table */}
-
-
-
Branch Revenue Details
-
-
-
-
-
- | Branch |
- Appointments |
- Total Revenue |
-
-
-
- {data.map((branch, i) => (
-
- | {branch.branch_name || 'Unknown'} |
- {branch.appointment_count || 0} |
- Rs. {(Number(branch.total_revenue) || 0).toLocaleString()} |
-
- ))}
-
-
-
-
-
- );
- }
-
- case 'doctor-revenue': {
- // reportData is an array directly from the API
- const data = Array.isArray(reportData) ? reportData : [];
-
- const chartData = {
- labels: data.map(d => d.doctor_name || 'Unknown Doctor'),
- datasets: [
- {
- label: 'Revenue (Rs.)',
- data: data.map(d => Number(d.doctor_revenue) || 0),
- backgroundColor: 'rgba(59,130,246,0.8)',
- borderColor: 'rgba(59,130,246,1)',
- borderWidth: 1
+ if (context.parsed.y !== null) {
+ label += `Rs. ${context.parsed.y.toLocaleString()}`;
}
- ]
- };
-
- const chartOptions = {
- responsive: true,
- plugins: {
- legend: { position: 'top' },
- title: { display: true, text: 'Revenue by Doctor' }
- },
- scales: {
- y: { beginAtZero: true }
+ return label;
}
- };
-
- const totalRevenue = data.reduce((sum, doctor) => sum + (Number(doctor.doctor_revenue) || 0), 0);
- const totalPatients = data.reduce((sum, doctor) => sum + (Number(doctor.total_appointments) || 0), 0);
-
- return (
-
- {/* Summary Cards */}
-
-
-
-
-
Total Revenue
-
Rs. {totalRevenue.toLocaleString()}
-
-
- 💰
-
-
-
-
-
-
-
-
Active Doctors
-
{data.length}
-
-
- 👨⚕️
-
-
-
-
-
-
-
-
Total Appointments
-
{totalPatients.toLocaleString()}
-
-
- �
-
-
-
-
+ }
+ }
+ },
+ scales: {
+ x: {
+ stacked: true,
+ title: {
+ display: true,
+ text: 'Month',
+ font: { size: 12, weight: 'bold' }
+ },
+ grid: {
+ display: false
+ }
+ },
+ y: {
+ stacked: true,
+ beginAtZero: true,
+ title: {
+ display: true,
+ text: 'Amount (Rs.)',
+ font: { size: 12, weight: 'bold' }
+ }
+ }
+ }
+ };
- {/* Chart */}
-
-
-
+ // Calculate summary statistics
+ const summary = {
+ totalPayments: data.reduce((sum, item) => sum + Number(item.total_payments || 0), 0),
+ totalInsurance: data.reduce((sum, item) => sum + Number(item.total_insurance_covered || 0), 0),
+ totalOutOfPocket: data.reduce((sum, item) => sum + Number(item.total_out_of_pocket || 0), 0),
+ averageCoverage: data.length ?
+ (data.reduce((sum, item) => sum + Number(item.total_insurance_covered || 0), 0) /
+ data.reduce((sum, item) => sum + Number(item.total_payments || 0), 0) * 100).toFixed(1) : 0
+ };
- {/* Table */}
-
-
-
Doctor Revenue Details
-
-
-
-
-
- | Doctor |
- Appointments |
- Completed |
- Revenue |
- Outstanding |
-
-
-
- {data.map((doctor, i) => (
-
- | Dr. {doctor.doctor_name} |
- {doctor.total_appointments || 0} |
- {doctor.completed_appointments || 0} |
- Rs. {(Number(doctor.doctor_revenue) || 0).toLocaleString()} |
- Rs. {(Number(doctor.outstanding_amount) || 0).toLocaleString()} |
-
- ))}
-
-
+ return (
+
+
+
+
+
+
+
+
- );
- }
- case 'insurance-claims': {
- // Need to update the API endpoint to use insurance-analysis
- const data = Array.isArray(reportData) ? reportData : [];
-
- const chartData = {
- labels: data.map(c => {
- if (c.month_year) {
- const date = new Date(c.month_year);
- return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short' });
- }
- return 'Unknown';
- }),
- datasets: [
- {
- label: 'Total Appointments',
- data: data.map(c => Number(c.total_appointments) || 0),
- backgroundColor: 'rgba(59,130,246,0.8)',
- borderColor: 'rgba(59,130,246,1)',
- borderWidth: 1
- },
- {
- label: 'Insurance Covered (Rs.)',
- data: data.map(c => Number(c.total_insurance_covered) || 0),
- backgroundColor: 'rgba(139,92,246,0.8)',
- borderColor: 'rgba(139,92,246,1)',
- borderWidth: 1
- }
- ]
- };
-
- const chartOptions = {
- responsive: true,
- plugins: {
- legend: { position: 'top' },
- title: { display: true, text: 'Insurance Analysis by Month' }
- },
- scales: {
- y: { beginAtZero: true }
- }
- };
-
- const totalPayments = data.reduce((sum, item) => sum + (Number(item.total_payments) || 0), 0);
- const totalInsurance = data.reduce((sum, item) => sum + (Number(item.total_insurance_covered) || 0), 0);
- const totalOutOfPocket = data.reduce((sum, item) => sum + (Number(item.total_out_of_pocket) || 0), 0);
- const avgCoverage = data.length > 0 ? (totalInsurance / totalPayments * 100) : 0;
-
- return (
-
{/* Summary Cards */}
-
-
-
-
-
Total Payments
-
Rs. {totalPayments.toLocaleString()}
-
-
- �
-
-
+
+
+
Total Payments
+
Rs. {summary.totalPayments.toLocaleString()}
-
-
-
-
-
Insurance Covered
-
Rs. {totalInsurance.toLocaleString()}
-
-
- 🛡️
-
-
+
+
Insurance Covered
+
Rs. {summary.totalInsurance.toLocaleString()}
-
-
-
-
-
Out of Pocket
-
Rs. {totalOutOfPocket.toLocaleString()}
-
-
- �
-
-
+
+
Out of Pocket
+
Rs. {summary.totalOutOfPocket.toLocaleString()}
-
-
-
-
-
Coverage Rate
-
{avgCoverage.toFixed(1)}%
-
-
- �
-
-
+
+
Average Coverage
+
{summary.averageCoverage}%
{/* Chart */}
-
-
-
-
- {/* Table */}
-
-
-
Monthly Insurance Analysis
-
-
-
-
-
- | Month |
- Appointments |
- Total Payments |
- Insurance |
- Out of Pocket |
- Coverage % |
-
-
-
- {data.map((item, i) => (
-
- |
- {item.month_year ? new Date(item.month_year).toLocaleDateString('en-US', { year: 'numeric', month: 'long' }) : 'Unknown'}
- |
- {item.total_appointments || 0} |
- Rs. {(Number(item.total_payments) || 0).toLocaleString()} |
- Rs. {(Number(item.total_insurance_covered) || 0).toLocaleString()} |
- Rs. {(Number(item.total_out_of_pocket) || 0).toLocaleString()} |
-
-
- {(Number(item.overall_insurance_coverage_percentage) || 0).toFixed(1)}%
-
- |
-
- ))}
-
-
+ {loading ? (
+
-
-
- );
- }
-
- default:
- return (
-
-
-
Report Under Development
-
This report visualization is currently being developed.
-
- );
- }
- };
-
- return (
-
- {/* Fixed Sidebar */}
-
-
-
-
- {/* Main Content */}
-
-
- {/* Header */}
-
-
- 📊
- Management Reports
-
-
Comprehensive business intelligence and analytics
-
-
-
- {/* Report Selection Panel */}
-
-
-
- 📋
- Available Reports
-
-
-
- {reports.map((report) => (
-
setActiveReport(report.id)}
- >
-
-
{report.icon}
-
-
{report.title}
-
{report.description}
-
-
-
- ))}
-
-
- {/* Filters Panel */}
- {activeReport && reports.find(r => r.id === activeReport)?.type === 'date-range' && (
-
-
- 🔍
- Filters
-
-
-
-
- setFilters(prev => ({...prev, startDate: e.target.value}))}
- className="w-full border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
- />
-
-
-
-
- setFilters(prev => ({...prev, endDate: e.target.value}))}
- className="w-full border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
- />
-
-
-
-
-
- )}
+ ) : error ? (
+
{error}
+ ) : data.length === 0 ? (
+
+ No data available for selected period
-
-
- {/* Report Display Panel */}
-
-
- {activeReport && (
-
-
-
-
{reports.find(r => r.id === activeReport)?.icon}
-
-
- {reports.find(r => r.id === activeReport)?.title}
-
-
- {reports.find(r => r.id === activeReport)?.description}
-
-
-
-
-
-
- )}
-
- {renderReportData()}
+ ) : (
+
+
-
+ )}
);
-}
+}
\ No newline at end of file