feat: add new frontend components, MH batch worker, and gitignore rules

- Add all new Frontend source files (pages, components, hooks, utils)
- Add selenium_MHBatchPaymentCheckWorker.py and MHSinglePaymentCheckWorker.py
- Add install-steps-5-13.sh setup script
- Update .gitignore to exclude runtime/sensitive data (backups, uploads,
  chat-history, keys, downloads, generated .d.ts files) while keeping folders
- Add .gitkeep to preserve empty runtime folders in git

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 00:23:43 -04:00
co-authored by Claude Sonnet 4.6
parent b7e06adf2f
commit 1edf73fdc8
173 changed files with 33469 additions and 0 deletions
@@ -0,0 +1,58 @@
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, ResponsiveContainer, Tooltip, } from "recharts";
export function AppointmentsByDay({ appointments }) {
const daysOfWeek = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const countsByDay = daysOfWeek.map((day) => ({ day, count: 0 }));
// Get current date and set time to start of day (midnight)
const now = new Date();
now.setHours(0, 0, 0, 0);
// Calculate Monday of the current week
const day = now.getDay(); // 0 = Sunday, 1 = Monday, ...
const diffToMonday = day === 0 ? -6 : 1 - day; // adjust if Sunday
const monday = new Date(now);
monday.setDate(now.getDate() + diffToMonday);
// Sunday of the current week
const sunday = new Date(monday);
sunday.setDate(monday.getDate() + 6);
// Filter appointments only from this week (Monday to Sunday)
const appointmentsThisWeek = appointments.filter((appointment) => {
if (!appointment.date)
return false;
const date = new Date(appointment.date);
// Reset time to compare just the date
date.setHours(0, 0, 0, 0);
return date >= monday && date <= sunday;
});
// Count appointments by day for current week
appointmentsThisWeek.forEach((appointment) => {
const date = new Date(appointment.date);
const dayOfWeek = date.getDay(); // 0 = Sunday, 1 = Monday, ...
const dayIndex = dayOfWeek === 0 ? 6 : dayOfWeek - 1; // Monday=0, Sunday=6
if (countsByDay[dayIndex]) {
countsByDay[dayIndex].count += 1;
}
});
return (<Card className="shadow-sm">
<CardHeader className="pb-2">
<CardTitle className="text-base font-medium">
Appointments by Day
</CardTitle>
<p className="text-xs text-muted-foreground">
Distribution of appointments throughout the week
</p>
</CardHeader>
<CardContent>
<div className="h-[200px]">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={countsByDay} margin={{ top: 5, right: 5, left: 0, bottom: 5 }}>
<CartesianGrid strokeDasharray="3 3" vertical={false}/>
<XAxis dataKey="day" fontSize={12} tickLine={false} axisLine={false}/>
<YAxis fontSize={12} tickLine={false} axisLine={false}/>
<Tooltip formatter={(value) => [`${value} appointments`, "Count"]} labelFormatter={(value) => `${value}`}/>
<Bar dataKey="count" fill="#2563eb" radius={[4, 4, 0, 0]}/>
</BarChart>
</ResponsiveContainer>
</div>
</CardContent>
</Card>);
}
@@ -0,0 +1,47 @@
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { LineChart, Line, XAxis, YAxis, CartesianGrid, ResponsiveContainer, Tooltip } from "recharts";
export function NewPatients({ patients }) {
// Get months for the chart
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
// Process patient data by registration month
const patientsByMonth = months.map(month => ({ name: month, count: 0 }));
// Count new patients by month
patients.forEach(patient => {
const createdDate = new Date(patient.createdAt);
const monthIndex = createdDate.getMonth();
if (patientsByMonth[monthIndex]) {
patientsByMonth[monthIndex].count += 1;
}
});
// Add some sample data for visual effect if no patients
if (patients.length === 0) {
// Sample data pattern similar to the screenshot
const sampleData = [17, 12, 22, 16, 15, 17, 22, 28, 20, 16];
sampleData.forEach((value, index) => {
if (index < patientsByMonth.length) {
if (patientsByMonth[index]) {
patientsByMonth[index].count = value;
}
}
});
}
return (<Card className="shadow-sm">
<CardHeader className="pb-2">
<CardTitle className="text-base font-medium">New Patients</CardTitle>
<p className="text-xs text-muted-foreground">Monthly trend of new patient registrations</p>
</CardHeader>
<CardContent>
<div className="h-[200px]">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={patientsByMonth} margin={{ top: 5, right: 5, left: 0, bottom: 5 }}>
<CartesianGrid strokeDasharray="3 3" vertical={false}/>
<XAxis dataKey="name" fontSize={12} tickLine={false} axisLine={false}/>
<YAxis fontSize={12} tickLine={false} axisLine={false}/>
<Tooltip formatter={(value) => [`${value} patients`, "Count"]} labelFormatter={(value) => `${value}`}/>
<Line type="monotone" dataKey="count" stroke="#f97316" strokeWidth={2} dot={{ r: 4, strokeWidth: 2 }} activeDot={{ r: 6, strokeWidth: 2 }}/>
</LineChart>
</ResponsiveContainer>
</div>
</CardContent>
</Card>);
}