Changeset 9af201e for frontend/src/pages/billing/BillingDetail.js
- Timestamp:
- 09/04/26 19:08:08 (3 weeks ago)
- Branches:
- master
- Children:
- cdcff72
- Parents:
- e1f74f6
- File:
-
- 1 edited
-
frontend/src/pages/billing/BillingDetail.js (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
frontend/src/pages/billing/BillingDetail.js
re1f74f6 r9af201e 22 22 23 23 useEffect(() => { 24 fetchBilling(); 25 }, [id]); 26 27 const fetchBilling = async () => { 28 try { 29 setLoading(true); 30 const response = await billingService.getBillingById(id); 24 const fetchBilling = async () => { 25 try { 26 setLoading(true); 27 const response = await billingService.getBillingById(id); 31 28 setBilling(response.data); 32 29 setPaymentStatus(response.data.paymentStatus); … … 39 36 console.error('Could not fetch billing details:', err); 40 37 } 41 } catch (err) { 42 setError('Failed to fetch billing record'); 43 console.error(err); 44 } finally { 45 setLoading(false); 46 } 47 }; 38 } catch (err) { 39 setError('Failed to fetch billing record'); 40 console.error(err); 41 } finally { 42 setLoading(false); 43 } 44 }; 45 46 fetchBilling(); 47 }, [id]); 48 48 49 49 const handleUpdatePaymentStatus = async () => { … … 81 81 if (user.role === 'DOCTOR' || user.role === 'LAB_TECHNICIAN') { 82 82 return ( 83 <div style={{ padding: '20px', textAlign: 'center' }}>84 <h1 className="text-2xl font-bold" style={{ color: '#7c3aed', marginBottom: '10px' }}>Access Denied</h1>85 <p style={{ color: 'var(--color-neutral-600)' }}>You do not have permission to access billing records.</p>86 </div>83 <div style={{ padding: '20px', textAlign: 'center' }}> 84 <h1 className="text-2xl font-bold" style={{ color: '#7c3aed', marginBottom: '10px' }}>Access Denied</h1> 85 <p style={{ color: 'var(--color-neutral-600)' }}>You do not have permission to access billing records.</p> 86 </div> 87 87 ); 88 88 } … … 90 90 if (!billing) { 91 91 return ( 92 <div>93 <ErrorAlert message="Billing record not found" onClose={() => navigate('/billing')} />94 </div>92 <div> 93 <ErrorAlert message="Billing record not found" onClose={() => navigate('/billing')} /> 94 </div> 95 95 ); 96 96 } 97 97 98 98 return ( 99 <div> 100 <div className="flex justify-between items-center mb-6"> 101 <h1 className="text-3xl font-bold" style={{ color: '#7c3aed' }}>Billing Record #{billing.billId}</h1> 102 <div className="space-x-2"> 103 <button 104 onClick={handleDownloadInvoice} 105 disabled={downloading} 106 className="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700 disabled:opacity-50" 107 > 108 {downloading ? 'Downloading...' : 'Download Invoice (PDF)'} 109 </button> 110 <button onClick={() => navigate('/billing')} className="bg-gray-300 text-gray-700 px-4 py-2 rounded hover:bg-gray-400"> 111 Back 112 </button> 113 </div> 99 <div> 100 <div className="flex justify-between items-center mb-6"> 101 <h1 className="text-3xl font-bold" style={{ color: '#7c3aed' }}>Billing Record #{billing.billId}</h1> 102 <div className="space-x-2"> 103 <button 104 onClick={handleDownloadInvoice} 105 disabled={downloading} 106 className="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700 disabled:opacity-50" 107 > 108 {downloading ? 'Downloading...' : 'Download Invoice (PDF)'} 109 </button> 110 <button onClick={() => navigate('/billing')} className="bg-gray-300 text-gray-700 px-4 py-2 rounded hover:bg-gray-400"> 111 Back 112 </button> 114 113 </div> 115 116 {error && <ErrorAlert message={error} onClose={() => setError(null)} />} 117 {success && <SuccessAlert message={success} onClose={() => setSuccess(null)} />} 118 119 <div className={`grid gap-6 mb-6 ${isPatient ? 'grid-cols-1 lg:grid-cols-2' : 'grid-cols-1 lg:grid-cols-3'}`}> 114 </div> 115 116 {error && <ErrorAlert message={error} onClose={() => setError(null)} />} 117 {success && <SuccessAlert message={success} onClose={() => setSuccess(null)} />} 118 119 <div className={`grid gap-6 mb-6 ${isPatient ? 'grid-cols-1 lg:grid-cols-2' : 'grid-cols-1 lg:grid-cols-3'}`}> 120 <div className="bg-white rounded-lg shadow p-6"> 121 <h2 className="text-xl font-bold mb-4">Billing Information</h2> 122 <div className="space-y-3"> 123 <InfoRow label="Patient" value={billing.patientName} /> 124 <InfoRow label="Total Cost" value={`$${billing.totalCost}`} /> 125 <InfoRow label="Current Status" value={billing.paymentStatus} /> 126 <InfoRow label="Payment Date" value={billing.paymentDate || 'Not paid'} /> 127 </div> 128 </div> 129 130 {!isPatient && ( 120 131 <div className="bg-white rounded-lg shadow p-6"> 121 <h2 className="text-xl font-bold mb-4">Billing Information</h2> 122 <div className="space-y-3"> 123 <InfoRow label="Patient" value={billing.patientName} /> 124 <InfoRow label="Total Cost" value={`$${billing.totalCost}`} /> 125 <InfoRow label="Current Status" value={billing.paymentStatus} /> 126 <InfoRow label="Payment Date" value={billing.paymentDate || 'Not paid'} /> 132 <h2 className="text-xl font-bold mb-4">Update Payment Status</h2> 133 <div className="space-y-4"> 134 <div> 135 <label className="block text-sm font-semibold mb-2">Payment Status</label> 136 <select 137 value={paymentStatus} 138 onChange={(e) => setPaymentStatus(e.target.value)} 139 className="w-full px-4 py-2 border rounded-lg" 140 > 141 <option value="PENDING">Pending</option> 142 <option value="PAID">Paid</option> 143 <option value="CANCELLED">Cancelled</option> 144 </select> 145 </div> 146 <button 147 onClick={handleUpdatePaymentStatus} 148 disabled={updating || paymentStatus === billing.paymentStatus} 149 className="w-full bg-purple-600 text-white px-4 py-2 rounded hover:bg-purple-700 disabled:opacity-50" 150 > 151 {updating ? 'Updating...' : 'Update Status'} 152 </button> 127 153 </div> 128 154 </div> 129 130 {!isPatient && ( 131 <div className="bg-white rounded-lg shadow p-6"> 132 <h2 className="text-xl font-bold mb-4">Update Payment Status</h2> 133 <div className="space-y-4"> 134 <div> 135 <label className="block text-sm font-semibold mb-2">Payment Status</label> 136 <select 137 value={paymentStatus} 138 onChange={(e) => setPaymentStatus(e.target.value)} 139 className="w-full px-4 py-2 border rounded-lg" 140 > 141 <option value="PENDING">Pending</option> 142 <option value="PAID">Paid</option> 143 <option value="CANCELLED">Cancelled</option> 144 </select> 145 </div> 146 <button 147 onClick={handleUpdatePaymentStatus} 148 disabled={updating || paymentStatus === billing.paymentStatus} 149 className="w-full bg-purple-600 text-white px-4 py-2 rounded hover:bg-purple-700 disabled:opacity-50" 150 > 151 {updating ? 'Updating...' : 'Update Status'} 152 </button> 153 </div> 154 </div> 155 )} 156 157 <div className="bg-white rounded-lg shadow p-6"> 158 <h2 className="text-xl font-bold mb-4">Patient Details</h2> 159 <div className="space-y-3"> 160 {billingDetail && ( 161 <> 162 <InfoRow label="EMBG" value={billingDetail.patientEmbg} /> 163 <InfoRow label="Phone" value={billingDetail.patientPhone} /> 164 <InfoRow label="Bill Date" value={billingDetail.billDate} /> 165 </> 166 )} 155 )} 156 157 <div className="bg-white rounded-lg shadow p-6"> 158 <h2 className="text-xl font-bold mb-4">Patient Details</h2> 159 <div className="space-y-3"> 160 {billingDetail && ( 161 <> 162 <InfoRow label="EMBG" value={billingDetail.patientEmbg} /> 163 <InfoRow label="Phone" value={billingDetail.patientPhone} /> 164 <InfoRow label="Bill Date" value={billingDetail.billDate} /> 165 </> 166 )} 167 </div> 168 </div> 169 </div> 170 171 <div className="bg-white rounded-lg shadow p-6"> 172 <h2 className="text-xl font-bold mb-4">Itemized Services</h2> 173 174 {billingDetail && (billingDetail.procedures.length > 0 || billingDetail.labTests.length > 0) ? ( 175 <div className="space-y-4"> 176 {billingDetail.procedures.length > 0 && ( 177 <div> 178 <h3 className="font-semibold text-lg mb-2">Procedures</h3> 179 <table className="w-full"> 180 <thead className="bg-gray-100"> 181 <tr> 182 <th className="px-4 py-2 text-left">Description</th> 183 <th className="px-4 py-2 text-right">Cost</th> 184 </tr> 185 </thead> 186 <tbody> 187 {billingDetail.procedures.map((proc, idx) => ( 188 <tr key={idx} className="border-t"> 189 <td className="px-4 py-2">{proc.description}</td> 190 <td className="px-4 py-2 text-right">${proc.cost}</td> 191 </tr> 192 ))} 193 </tbody> 194 </table> 195 </div> 196 )} 197 198 {billingDetail.labTests.length > 0 && ( 199 <div> 200 <h3 className="font-semibold text-lg mb-2">Lab Tests</h3> 201 <table className="w-full"> 202 <thead className="bg-gray-100"> 203 <tr> 204 <th className="px-4 py-2 text-left">Description</th> 205 <th className="px-4 py-2 text-right">Cost</th> 206 </tr> 207 </thead> 208 <tbody> 209 {billingDetail.labTests.map((test, idx) => ( 210 <tr key={idx} className="border-t"> 211 <td className="px-4 py-2">{test.description}</td> 212 <td className="px-4 py-2 text-right">${test.cost}</td> 213 </tr> 214 ))} 215 </tbody> 216 </table> 217 </div> 218 )} 219 220 <div className="border-t-2 pt-4 mt-4 flex justify-end"> 221 <div className="text-right"> 222 <p className="text-gray-600">Total Amount:</p> 223 <p className="text-2xl font-bold text-purple-600">${billing.totalCost}</p> 224 </div> 167 225 </div> 168 226 </div> 169 </div> 170 171 <div className="bg-white rounded-lg shadow p-6"> 172 <h2 className="text-xl font-bold mb-4">Itemized Services</h2> 173 174 {billingDetail && (billingDetail.procedures.length > 0 || billingDetail.labTests.length > 0) ? ( 175 <div className="space-y-4"> 176 {billingDetail.procedures.length > 0 && ( 177 <div> 178 <h3 className="font-semibold text-lg mb-2">Procedures</h3> 179 <table className="w-full"> 180 <thead className="bg-gray-100"> 181 <tr> 182 <th className="px-4 py-2 text-left">Description</th> 183 <th className="px-4 py-2 text-right">Cost</th> 184 </tr> 185 </thead> 186 <tbody> 187 {billingDetail.procedures.map((proc, idx) => ( 188 <tr key={idx} className="border-t"> 189 <td className="px-4 py-2">{proc.description}</td> 190 <td className="px-4 py-2 text-right">${proc.cost}</td> 191 </tr> 192 ))} 193 </tbody> 194 </table> 195 </div> 196 )} 197 198 {billingDetail.labTests.length > 0 && ( 199 <div> 200 <h3 className="font-semibold text-lg mb-2">Lab Tests</h3> 201 <table className="w-full"> 202 <thead className="bg-gray-100"> 203 <tr> 204 <th className="px-4 py-2 text-left">Description</th> 205 <th className="px-4 py-2 text-right">Cost</th> 206 </tr> 207 </thead> 208 <tbody> 209 {billingDetail.labTests.map((test, idx) => ( 210 <tr key={idx} className="border-t"> 211 <td className="px-4 py-2">{test.description}</td> 212 <td className="px-4 py-2 text-right">${test.cost}</td> 213 </tr> 214 ))} 215 </tbody> 216 </table> 217 </div> 218 )} 219 220 <div className="border-t-2 pt-4 mt-4 flex justify-end"> 221 <div className="text-right"> 222 <p className="text-gray-600">Total Amount:</p> 223 <p className="text-2xl font-bold text-purple-600">${billing.totalCost}</p> 224 </div> 225 </div> 226 </div> 227 ) : ( 228 <p className="text-gray-500">No services itemized for this billing record.</p> 229 )} 230 </div> 231 </div> 227 ) : ( 228 <p className="text-gray-500">No services itemized for this billing record.</p> 229 )} 230 </div> 231 </div> 232 232 ); 233 233 } … … 235 235 function InfoRow({ label, value }) { 236 236 return ( 237 <div className="flex justify-between">238 <span className="text-gray-800">{label}:</span>239 <span className="text-gray-800">{value || 'N/A'}</span>240 </div>237 <div className="flex justify-between"> 238 <span className="text-gray-800">{label}:</span> 239 <span className="text-gray-800">{value || 'N/A'}</span> 240 </div> 241 241 ); 242 242 }
Note:
See TracChangeset
for help on using the changeset viewer.
