mirror of
https://github.com/temetro/temetro.git
synced 2026-08-28 11:27:40 +00:00
Wire frontend to the backend: auth, organizations, real patient API
Connects the UI-only app to the new backend over Better Auth + a small patient API client, replacing the in-memory fixture and placeholder identity. - Better Auth React client (lib/auth-client.ts) + shared access-control roles; API client (lib/api-client.ts) sending credentials cross-origin. - Designed (auth) route group: login, signup, verify-email, forgot/reset password, clinic onboarding, and accept-invite. - proxy.ts (this Next's renamed middleware) optimistic redirect + an authoritative client AppAuthGuard requiring a session and active clinic. - Real identity in nav-user (useSession + sign out); the unused team switcher repurposed as an organization (clinic) switcher; Care-team settings tab manages members and invitations. - lib/patients.ts now calls the org-scoped API (types unchanged); patients table and create/edit dialog updated for async create/update. - next.config: standalone output + Dockerfile for containerized runs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import { Plus, Search } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { PatientFormDialog } from "@/components/chat/patient-form-dialog";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
@@ -25,8 +25,35 @@ export function PatientsView() {
|
||||
// Bumped on open so the create dialog remounts with a fresh file # / form.
|
||||
const [addKey, setAddKey] = useState(0);
|
||||
|
||||
const [allPatients, setAllPatients] = useState<Patient[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [loadError, setLoadError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
setLoading(true);
|
||||
listPatients()
|
||||
.then((data) => {
|
||||
if (!active) return;
|
||||
setAllPatients(data);
|
||||
setLoadError(null);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (!active) return;
|
||||
setLoadError(
|
||||
err instanceof Error ? err.message : "Failed to load patients."
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
if (active) setLoading(false);
|
||||
});
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const q = query.trim().toLowerCase();
|
||||
const patients = listPatients().filter(
|
||||
const patients = allPatients.filter(
|
||||
(p) => !q || p.name.toLowerCase().includes(q) || p.fileNumber.includes(q)
|
||||
);
|
||||
|
||||
@@ -73,7 +100,22 @@ export function PatientsView() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{patients.length === 0 ? (
|
||||
{loading ? (
|
||||
<tr>
|
||||
<td
|
||||
className="px-4 py-10 text-center text-muted-foreground"
|
||||
colSpan={6}
|
||||
>
|
||||
Loading patients…
|
||||
</td>
|
||||
</tr>
|
||||
) : loadError ? (
|
||||
<tr>
|
||||
<td className="px-4 py-10 text-center text-destructive" colSpan={6}>
|
||||
{loadError}
|
||||
</td>
|
||||
</tr>
|
||||
) : patients.length === 0 ? (
|
||||
<tr>
|
||||
<td
|
||||
className="px-4 py-10 text-center text-muted-foreground"
|
||||
|
||||
Reference in New Issue
Block a user