fix: add patient form validation and auth proxy
- Fix dateOfBirth default from empty string to null (caused Invalid date error) - Add noValidate to form to prevent browser native email validation blocking submit - Reset form when switching from edit to add mode - Export API_BASE_URL from queryClient; switch patient table to raw fetch (prevents token wipe on 401) - Add Authorization header forwarding in Vite proxy (was stripped by nginx Connection:upgrade) - Make only firstName, lastName, dateOfBirth, phone required; gender optional - Add +1 prefix to phone number input (stores as 1XXXXXXXXXX) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -67,13 +67,13 @@ export const PatientForm = forwardRef<PatientFormRef, PatientFormProps>(
|
||||
...sanitizedPatient,
|
||||
dateOfBirth: patient.dateOfBirth
|
||||
? formatLocalDate(new Date(patient.dateOfBirth))
|
||||
: "",
|
||||
: null,
|
||||
};
|
||||
}
|
||||
return {
|
||||
firstName: extractedInfo?.firstName || "",
|
||||
lastName: extractedInfo?.lastName || "",
|
||||
dateOfBirth: extractedInfo?.dateOfBirth || "",
|
||||
dateOfBirth: extractedInfo?.dateOfBirth || null,
|
||||
gender: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
@@ -119,9 +119,11 @@ export const PatientForm = forwardRef<PatientFormRef, PatientFormProps>(
|
||||
...sanitizedPatient,
|
||||
dateOfBirth: patient.dateOfBirth
|
||||
? formatLocalDate(new Date(patient.dateOfBirth))
|
||||
: "",
|
||||
: null,
|
||||
};
|
||||
form.reset(resetValues);
|
||||
} else {
|
||||
form.reset(computedDefaultValues);
|
||||
}
|
||||
}, [patient, computedDefaultValues, form]);
|
||||
|
||||
@@ -134,6 +136,7 @@ export const PatientForm = forwardRef<PatientFormRef, PatientFormProps>(
|
||||
<form
|
||||
id="patient-form"
|
||||
key={patient?.id || "new"}
|
||||
noValidate
|
||||
onSubmit={form.handleSubmit((data) => {
|
||||
handleSubmit2(data);
|
||||
})}
|
||||
@@ -185,7 +188,7 @@ export const PatientForm = forwardRef<PatientFormRef, PatientFormProps>(
|
||||
name="gender"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Gender *</FormLabel>
|
||||
<FormLabel>Gender</FormLabel>
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value as string}
|
||||
@@ -217,15 +220,36 @@ export const PatientForm = forwardRef<PatientFormRef, PatientFormProps>(
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="phone"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Phone Number *</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
render={({ field }) => {
|
||||
const display = (field.value as string)?.startsWith("1")
|
||||
? (field.value as string).slice(1)
|
||||
: (field.value as string) || "";
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>Phone Number *</FormLabel>
|
||||
<FormControl>
|
||||
<div className="flex">
|
||||
<span className="inline-flex items-center px-3 rounded-l-md border border-r-0 border-input bg-muted text-muted-foreground text-sm">
|
||||
+1
|
||||
</span>
|
||||
<Input
|
||||
className="rounded-l-none"
|
||||
placeholder="6175551234"
|
||||
value={display}
|
||||
onChange={(e) => {
|
||||
const digits = e.target.value.replace(/\D/g, "").slice(0, 10);
|
||||
field.onChange(digits ? "1" + digits : "");
|
||||
}}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
@@ -310,7 +334,7 @@ export const PatientForm = forwardRef<PatientFormRef, PatientFormProps>(
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>Status *</FormLabel>
|
||||
<FormLabel>Status</FormLabel>
|
||||
<Select
|
||||
value={(field.value as PatientStatus) ?? "UNKNOWN"}
|
||||
onValueChange={(v) =>
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
} from "@/components/ui/pagination";
|
||||
import { apiRequest, queryClient } from "@/lib/queryClient";
|
||||
import { apiRequest, queryClient, API_BASE_URL } from "@/lib/queryClient";
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import LoadingScreen from "@/components/ui/LoadingScreen";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
@@ -749,11 +749,21 @@ export function PatientTable({
|
||||
url = `/api/patients/recent?limit=${patientsPerPage}&offset=${offset}`;
|
||||
}
|
||||
|
||||
const res = await apiRequest("GET", url);
|
||||
const token = localStorage.getItem("token");
|
||||
const res = await fetch(`${API_BASE_URL}${url}`, {
|
||||
headers: {
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
},
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (res.status === 401 || res.status === 403) {
|
||||
return { patients: [], totalCount: 0 };
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
const errorData = await res.json();
|
||||
throw new Error(errorData.message || "Search failed");
|
||||
const errorData = await res.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || "Failed to load patients");
|
||||
}
|
||||
|
||||
return res.json();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { QueryClient, QueryFunction } from "@tanstack/react-query";
|
||||
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL_BACKEND ?? "";
|
||||
export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL_BACKEND ?? "";
|
||||
|
||||
async function throwIfResNotOk(res: Response) {
|
||||
if (!res.ok) {
|
||||
|
||||
@@ -18,6 +18,12 @@ export default defineConfig(({ mode }) => {
|
||||
"/api": {
|
||||
target: env.VITE_API_BASE_URL_BACKEND || "http://localhost:5000",
|
||||
changeOrigin: true,
|
||||
configure: (proxy) => {
|
||||
proxy.on("proxyReq", (proxyReq, req) => {
|
||||
const auth = (req as any).headers["authorization"];
|
||||
if (auth) proxyReq.setHeader("Authorization", auth);
|
||||
});
|
||||
},
|
||||
},
|
||||
"/socket.io": {
|
||||
target: env.VITE_API_BASE_URL_BACKEND || "http://localhost:5000",
|
||||
|
||||
Reference in New Issue
Block a user