150 lines
4.0 KiB
TypeScript
150 lines
4.0 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
import { apiRequest } from "@/lib/queryClient";
|
|
import { toast } from "@/hooks/use-toast";
|
|
|
|
type Props = {
|
|
onClose: () => void;
|
|
defaultValues?: {
|
|
id?: number;
|
|
npiNumber: string;
|
|
providerName: string;
|
|
};
|
|
};
|
|
|
|
export function NpiProviderForm({ onClose, defaultValues }: Props) {
|
|
const [npiNumber, setNpiNumber] = useState(
|
|
defaultValues?.npiNumber || ""
|
|
);
|
|
const [providerName, setProviderName] = useState(
|
|
defaultValues?.providerName || ""
|
|
);
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
const mutation = useMutation({
|
|
mutationFn: async () => {
|
|
const payload = {
|
|
npiNumber: npiNumber.trim(),
|
|
providerName: providerName.trim(),
|
|
};
|
|
|
|
const url = defaultValues?.id
|
|
? `/api/npiProviders/${defaultValues.id}`
|
|
: "/api/npiProviders/";
|
|
|
|
const method = defaultValues?.id ? "PUT" : "POST";
|
|
|
|
const res = await apiRequest(method, url, payload);
|
|
|
|
if (!res.ok) {
|
|
const err = await res.json().catch(() => null);
|
|
throw new Error(err?.message || "Failed to save NPI provider");
|
|
}
|
|
|
|
return res.json();
|
|
},
|
|
onSuccess: () => {
|
|
toast({
|
|
title: `NPI provider ${
|
|
defaultValues?.id ? "updated" : "created"
|
|
}.`,
|
|
});
|
|
queryClient.invalidateQueries({
|
|
queryKey: ["/api/npiProviders/"],
|
|
});
|
|
onClose();
|
|
},
|
|
onError: (error: any) => {
|
|
toast({
|
|
title: "Error",
|
|
description: error.message,
|
|
variant: "destructive",
|
|
});
|
|
},
|
|
});
|
|
|
|
useEffect(() => {
|
|
setNpiNumber(defaultValues?.npiNumber || "");
|
|
setProviderName(defaultValues?.providerName || "");
|
|
}, [defaultValues]);
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
if (!npiNumber || !providerName) {
|
|
toast({
|
|
title: "Error",
|
|
description: "All fields are required.",
|
|
variant: "destructive",
|
|
});
|
|
return;
|
|
}
|
|
|
|
mutation.mutate();
|
|
};
|
|
|
|
return (
|
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
|
<div className="bg-white rounded-lg p-6 w-full max-w-md shadow-lg">
|
|
<h2 className="text-lg font-bold mb-4">
|
|
{defaultValues?.id
|
|
? "Edit NPI Provider"
|
|
: "Create NPI Provider"}
|
|
</h2>
|
|
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
<div>
|
|
<label className="block text-sm font-medium">
|
|
NPI Number
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={npiNumber}
|
|
onChange={(e) => setNpiNumber(e.target.value)}
|
|
className="mt-1 p-2 border rounded w-full"
|
|
placeholder="e.g., 1489890992"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium">
|
|
Provider Name
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={providerName}
|
|
onChange={(e) => setProviderName(e.target.value)}
|
|
className="mt-1 p-2 border rounded w-full"
|
|
placeholder="e.g., Kai Gao"
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex justify-end gap-2">
|
|
<button
|
|
type="button"
|
|
onClick={onClose}
|
|
className="text-gray-600 hover:underline"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
disabled={mutation.isPending}
|
|
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 disabled:opacity-50"
|
|
>
|
|
{mutation.isPending
|
|
? defaultValues?.id
|
|
? "Updating..."
|
|
: "Creating..."
|
|
: defaultValues?.id
|
|
? "Update"
|
|
: "Create"}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|