feat: add Auto Check MH Payment toggle with weekly schedule

Adds a toggle behind the Go button on the Payments page to automatically run the MH batch payment check on a user-selected day of week and hour. Default is off.

- Schema: added autoMhCheckEnabled, autoMhCheckDayOfWeek, autoMhCheckHour to User model
- Backend: new mhBatchPaymentService (shared logic), GET/PUT /auto-mh-check-setting routes, hourly cron job that fires on matching day+hour
- Frontend: toggle + day select (Mon–Sun) + time select (hourly) that persist immediately to DB

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 22:58:36 -04:00
parent 27d9132820
commit fc3e8c0e25
310 changed files with 2821 additions and 2015 deletions

View File

@@ -7,6 +7,8 @@ import {
CardDescription,
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
import { DollarSign, CalendarIcon } from "lucide-react";
import {
Select,
@@ -31,6 +33,21 @@ import { apiRequest } from "@/lib/queryClient";
import { toast } from "@/hooks/use-toast";
import PaymentEditModal from "@/components/payments/payment-edit-modal";
const DAYS_OF_WEEK = [
{ label: "Sunday", value: 0 },
{ label: "Monday", value: 1 },
{ label: "Tuesday", value: 2 },
{ label: "Wednesday", value: 3 },
{ label: "Thursday", value: 4 },
{ label: "Friday", value: 5 },
{ label: "Saturday", value: 6 },
];
const HOURS = Array.from({ length: 24 }, (_, i) => ({
label: i === 0 ? "12:00 AM" : i < 12 ? `${i}:00 AM` : i === 12 ? "12:00 PM" : `${i - 12}:00 PM`,
value: i,
}));
function formatDateInput(raw: string): string {
const digits = raw.replace(/\D/g, "").slice(0, 8);
if (digits.length <= 2) return digits;
@@ -41,6 +58,12 @@ function formatDateInput(raw: string): string {
export default function PaymentsPage() {
const [paymentPeriod, setPaymentPeriod] = useState<string>("all-time");
// Auto MH check schedule
const [autoMhCheckEnabled, setAutoMhCheckEnabled] = useState(false);
const [autoMhCheckDay, setAutoMhCheckDay] = useState(1);
const [autoMhCheckHour, setAutoMhCheckHour] = useState(13);
const [autoMhCheckLoaded, setAutoMhCheckLoaded] = useState(false);
// Check Payments Online date range
const [mhFromDate, setMhFromDate] = useState<Date | undefined>(undefined);
const [mhToDate, setMhToDate] = useState<Date | undefined>(undefined);
@@ -147,6 +170,41 @@ export default function PaymentsPage() {
clearUrlParams(["paymentId", "patientId"]);
}, [location]);
// Load auto MH check setting on mount
useEffect(() => {
(async () => {
try {
const res = await apiRequest("GET", "/api/database-management/auto-mh-check-setting");
if (res.ok) {
const data = await res.json();
setAutoMhCheckEnabled(data.autoMhCheckEnabled ?? false);
setAutoMhCheckDay(data.autoMhCheckDayOfWeek ?? 1);
setAutoMhCheckHour(data.autoMhCheckHour ?? 13);
}
} catch {}
setAutoMhCheckLoaded(true);
})();
}, []);
const saveAutoMhCheckSetting = async (
enabled: boolean,
day: number,
hour: number
) => {
try {
const res = await apiRequest("PUT", "/api/database-management/auto-mh-check-setting", {
autoMhCheckEnabled: enabled,
autoMhCheckDayOfWeek: day,
autoMhCheckHour: hour,
});
if (!res.ok) {
toast({ title: "Failed to save auto MH check setting", variant: "destructive" });
}
} catch {
toast({ title: "Failed to save auto MH check setting", variant: "destructive" });
}
};
return (
<div>
{/* Header */}
@@ -358,6 +416,69 @@ export default function PaymentsPage() {
>
Go
</Button>
{/* Auto check MH payment toggle */}
{autoMhCheckLoaded && (
<div className="flex flex-wrap items-center gap-3 ml-2 pl-4 border-l border-gray-200">
<div className="flex items-center gap-2">
<Switch
id="auto-mh-check"
checked={autoMhCheckEnabled}
onCheckedChange={(checked) => {
setAutoMhCheckEnabled(checked);
saveAutoMhCheckSetting(checked, autoMhCheckDay, autoMhCheckHour);
}}
/>
<Label htmlFor="auto-mh-check" className="text-sm font-medium whitespace-nowrap cursor-pointer">
Auto Check MH Payment
</Label>
</div>
{autoMhCheckEnabled && (
<>
<Select
value={String(autoMhCheckDay)}
onValueChange={(v) => {
const day = Number(v);
setAutoMhCheckDay(day);
saveAutoMhCheckSetting(autoMhCheckEnabled, day, autoMhCheckHour);
}}
>
<SelectTrigger className="w-[140px]">
<SelectValue placeholder="Day" />
</SelectTrigger>
<SelectContent>
{DAYS_OF_WEEK.map((d) => (
<SelectItem key={d.value} value={String(d.value)}>
{d.label}
</SelectItem>
))}
</SelectContent>
</Select>
<Select
value={String(autoMhCheckHour)}
onValueChange={(v) => {
const hour = Number(v);
setAutoMhCheckHour(hour);
saveAutoMhCheckSetting(autoMhCheckEnabled, autoMhCheckDay, hour);
}}
>
<SelectTrigger className="w-[130px]">
<SelectValue placeholder="Time" />
</SelectTrigger>
<SelectContent>
{HOURS.map((h) => (
<SelectItem key={h.value} value={String(h.value)}>
{h.label}
</SelectItem>
))}
</SelectContent>
</Select>
</>
)}
</div>
)}
</div>
</CardContent>
</Card>