perf(connections): collapse pricing N+1 into a single bulk query
The /connections page was firing one listClinicPricing call per approved
counterpart inside Promise.all. That meant a lab with 10 clinics paid 10
sequential Appwrite roundtrips on every load, and worse, every time the
ClinicPricingDialog saved a row revalidatePath('/connections') ran the
whole fan-out again — saving a single discount felt like the request had
hung.
Replaced the per-peer query with listAllPricingForLab /
listAllPricingForClinic (single Query.equal on the side-specific column,
limit 500) and group the result into a Map client-side. One roundtrip
regardless of how many connections you have.
Also flipped the audit-log calls in setClinicPricingAction /
clearClinicPricingAction from 'await logAudit(...)' to 'void logAudit(...)'
— audit is best-effort by design and never blocks the user-facing
mutation; awaiting it doubled the perceived latency for nothing.
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { listClinicPricing } from "@/lib/appwrite/clinic-pricing-queries";
|
||||
import {
|
||||
listAllPricingForClinic,
|
||||
listAllPricingForLab,
|
||||
} from "@/lib/appwrite/clinic-pricing-queries";
|
||||
import {
|
||||
listApprovedConnections,
|
||||
listPendingInbound,
|
||||
@@ -37,29 +40,18 @@ export default async function ConnectionsPage() {
|
||||
listPendingOutbound(ctx.tenantId, ctx.user.id),
|
||||
]);
|
||||
|
||||
// Lab side: load pricing rules per approved clinic so the dialog can
|
||||
// surface existing rules. Clinic side leaves this empty — it's display-only
|
||||
// for them (see ConnectionsTable).
|
||||
// Single bulk query for pricing then group client-side. Avoids N+1 across
|
||||
// approved counterparts (was the main reason the save dialog felt slow —
|
||||
// every revalidatePath of /connections fanned out one query per peer).
|
||||
const allPricing = isLab
|
||||
? await listAllPricingForLab(ctx.tenantId)
|
||||
: await listAllPricingForClinic(ctx.tenantId);
|
||||
const pricingByCounterpart = new Map<string, ClinicPricing[]>();
|
||||
if (isLab) {
|
||||
const pricingLists = await Promise.all(
|
||||
approved.map((c) =>
|
||||
listClinicPricing(ctx.tenantId, c.clinicTenantId).then(
|
||||
(rows) => [c.clinicTenantId, rows] as const,
|
||||
),
|
||||
),
|
||||
);
|
||||
for (const [id, rows] of pricingLists) pricingByCounterpart.set(id, rows);
|
||||
} else {
|
||||
// Clinic side: only its own pricing rules from each lab, read-only
|
||||
const pricingLists = await Promise.all(
|
||||
approved.map((c) =>
|
||||
listClinicPricing(c.labTenantId, ctx.tenantId).then(
|
||||
(rows) => [c.labTenantId, rows] as const,
|
||||
),
|
||||
),
|
||||
);
|
||||
for (const [id, rows] of pricingLists) pricingByCounterpart.set(id, rows);
|
||||
for (const row of allPricing) {
|
||||
const key = isLab ? row.clinicTenantId : row.labTenantId;
|
||||
const bucket = pricingByCounterpart.get(key);
|
||||
if (bucket) bucket.push(row);
|
||||
else pricingByCounterpart.set(key, [row]);
|
||||
}
|
||||
const pricingObject: Record<string, ClinicPricing[]> = Object.fromEntries(
|
||||
pricingByCounterpart,
|
||||
|
||||
Reference in New Issue
Block a user