feat: implement core pharmacy management dashboard and navigation infrastructure

This commit is contained in:
2026-08-03 10:29:58 +05:30
parent bd800a3bab
commit 8c25b84616
18 changed files with 2184 additions and 184 deletions
+41 -35
View File
@@ -55,8 +55,8 @@ const columns = [
}
}),
columnHelper.accessor('price', {
header: 'Price',
size: 120,
header: 'Purchase Price / MRP',
size: 150,
cell: info => <span className="text-slate-800 font-medium text-[14px]">{info.getValue()}</span>
}),
columnHelper.accessor('manufacture', {
@@ -89,6 +89,7 @@ const Pharmacy = () => {
form: 'tablet',
strength: '',
mrp: '',
pack_size: '1',
schedule: 'H'
});
@@ -160,7 +161,7 @@ const Pharmacy = () => {
setIsAdding(true);
await addMedicine(addFormData);
setIsAddModalOpen(false);
setAddFormData({ brand_name: '', generic_name: '', manufacturer: '', form: 'tablet', strength: '', mrp: '', schedule: 'H' });
setAddFormData({ brand_name: '', generic_name: '', manufacturer: '', form: 'tablet', strength: '', mrp: '', pack_size: '1', schedule: 'H' });
await loadData();
} catch (err) {
console.error("Failed to add medicine", err);
@@ -177,8 +178,8 @@ const Pharmacy = () => {
subtitle: drug.generic_name || '',
badge: drug.schedule || '',
stock: parseInt(drug.total_stock) || 0,
rawPrice: parseFloat(drug.mrp) || 0,
price: new Intl.NumberFormat('en-IN', { style: 'currency', currency: 'INR' }).format(parseFloat(drug.mrp) || 0),
rawPrice: parseFloat(drug.purchase_price) || parseFloat(drug.mrp) || 0,
price: new Intl.NumberFormat('en-IN', { style: 'currency', currency: 'INR' }).format(parseFloat(drug.purchase_price) || parseFloat(drug.mrp) || 0),
manufacture: drug.manufacturer || 'Unknown',
type: drug.form || 'Unknown'
}));
@@ -250,39 +251,48 @@ const Pharmacy = () => {
return (
<div className="max-w-[1400px] mx-auto flex flex-col w-full">
<header className="mb-6 shrink-0 flex items-center justify-between">
<div>
<h1>Pharmacy Overview</h1>
<div className="flex flex-col mb-8 gap-2">
<div className="flex items-center gap-4">
<div className="bg-blue-100 p-3 rounded-xl flex items-center justify-center">
<Package className="text-blue-600" size={26} />
</div>
<h1 className="text-3xl font-bold text-slate-900 m-0 leading-none">Pharmacy Overview</h1>
</div>
</header>
<p className="text-slate-500 mt-2">Manage your pharmacy inventory, add medicines, and handle manual dispensing.</p>
</div>
{/* Metrics Row */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 shrink-0" style={{ marginBottom: '32px' }}>
<div className="bg-white rounded-xl p-5 border border-slate-200 shadow-sm flex items-center gap-4">
<div className="w-12 h-12 rounded-full bg-blue-50 flex items-center justify-center shrink-0">
<Package className="text-blue-600" size={24} />
<div className="bg-white rounded-2xl p-6 border border-slate-200 shadow-sm hover:shadow-md transition-shadow flex items-center gap-5">
<div className="p-4 rounded-2xl bg-gradient-to-br from-blue-50 to-blue-100 border border-blue-200/50 flex items-center justify-center shrink-0 shadow-sm">
<Package className="text-blue-600" size={28} />
</div>
<div>
<p className="text-slate-500 text-[13px] font-medium mb-1">Total Medicines</p>
<h3 className="text-2xl font-bold text-slate-900">{inventory.length}</h3>
<div className="flex flex-col">
<p className="text-slate-500 text-sm font-semibold tracking-wide uppercase mb-1">Total Medicines</p>
<h3 className="text-3xl font-extrabold text-slate-900">{inventory.length}</h3>
</div>
</div>
<div className="bg-white rounded-xl p-5 border border-slate-200 shadow-sm flex items-center gap-4">
<div className="w-12 h-12 rounded-full bg-red-50 flex items-center justify-center shrink-0">
<AlertCircle className="text-red-500" size={24} />
<div className="bg-white rounded-2xl p-6 border border-slate-200 shadow-sm hover:shadow-md transition-shadow flex items-center gap-5 relative overflow-hidden">
{lowStockCount > 0 && (
<div className="absolute top-0 right-0 w-2 h-full bg-red-500"></div>
)}
<div className="p-4 rounded-2xl bg-gradient-to-br from-red-50 to-red-100 border border-red-200/50 flex items-center justify-center shrink-0 shadow-sm">
<AlertCircle className="text-red-600" size={28} />
</div>
<div>
<p className="text-slate-500 text-[13px] font-medium mb-1">Low Stock Alerts</p>
<h3 className="text-2xl font-bold text-slate-900">{lowStockCount}</h3>
<div className="flex flex-col">
<p className="text-slate-500 text-sm font-semibold tracking-wide uppercase mb-1">Low Stock Alerts</p>
<h3 className={`text-3xl font-extrabold ${lowStockCount > 0 ? 'text-red-600' : 'text-slate-900'}`}>{lowStockCount}</h3>
</div>
</div>
<div className="bg-white rounded-xl p-5 border border-slate-200 shadow-sm flex items-center gap-4">
<div className="w-12 h-12 rounded-full bg-indigo-50 flex items-center justify-center shrink-0">
<Layers className="text-indigo-500" size={24} />
<div className="bg-white rounded-2xl p-6 border border-slate-200 shadow-sm hover:shadow-md transition-shadow flex items-center gap-5">
<div className="p-4 rounded-2xl bg-gradient-to-br from-indigo-50 to-indigo-100 border border-indigo-200/50 flex items-center justify-center shrink-0 shadow-sm">
<Layers className="text-indigo-600" size={28} />
</div>
<div>
<p className="text-slate-500 text-[13px] font-medium mb-1">Unique Categories</p>
<h3 className="text-2xl font-bold text-slate-900">{uniqueCategories}</h3>
<div className="flex flex-col">
<p className="text-slate-500 text-sm font-semibold tracking-wide uppercase mb-1">Unique Categories</p>
<h3 className="text-3xl font-extrabold text-slate-900">{uniqueCategories}</h3>
</div>
</div>
</div>
@@ -311,14 +321,6 @@ const Pharmacy = () => {
<Package size={18} strokeWidth={2.5} />
Manual Dispense
</button>
<button
onClick={() => setIsAddModalOpen(true)}
className="flex items-center gap-2 bg-blue-600 text-white px-5 py-2.5 rounded-xl font-semibold hover:bg-blue-700 transition-colors shadow-sm whitespace-nowrap"
>
<Plus size={18} strokeWidth={2.5} />
Add Medicine
</button>
</div>
</div>
@@ -422,6 +424,10 @@ const Pharmacy = () => {
<label className="block text-xs font-bold uppercase tracking-wider text-slate-500" style={{ marginBottom: '8px' }}>MRP (₹)</label>
<input required type="number" step="0.01" value={addFormData.mrp} onChange={e => setAddFormData({...addFormData, mrp: e.target.value})} className="w-full bg-slate-50 border border-slate-200 rounded-xl focus:bg-white focus:outline-none focus:ring-4 focus:ring-blue-500/10 focus:border-blue-500" placeholder="0.00" style={{ padding: '12px 16px' }} />
</div>
<div>
<label className="block text-xs font-bold uppercase tracking-wider text-slate-500" style={{ marginBottom: '8px' }}>Pack Size</label>
<input type="number" value={addFormData.pack_size} onChange={e => setAddFormData({...addFormData, pack_size: e.target.value})} className="w-full bg-slate-50 border border-slate-200 rounded-xl focus:bg-white focus:outline-none focus:ring-4 focus:ring-blue-500/10 focus:border-blue-500" placeholder="1" style={{ padding: '12px 16px' }} />
</div>
</div>
<div className="flex items-center justify-end border-t border-slate-100" style={{ paddingTop: '16px', gap: '12px' }}>