9c9568b80c
- Added GET /portal/conversation and GET /portal/conversation/messages endpoints - Created Communication.api.ts with typed fetchers and React hooks - Rewired Communication.tsx to use real API, removed mock data - Added composer-disabled bar with "Reply from your phone" tooltip - Added conversation route tests to portal.test.ts Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
66 lines
1.7 KiB
TypeScript
66 lines
1.7 KiB
TypeScript
import { eq } from "@groombook/db";
|
|
import { bufferTimeRules, services, type Db } from "@groombook/db";
|
|
|
|
export async function resolveBufferMinutes({
|
|
serviceId,
|
|
sizeCategory,
|
|
coatType,
|
|
db,
|
|
}: {
|
|
serviceId: string;
|
|
sizeCategory: string | null;
|
|
coatType: string | null;
|
|
db: Db;
|
|
}): Promise<number> {
|
|
// Query all rules for this service in one DB call
|
|
const allRules = await db
|
|
.select()
|
|
.from(bufferTimeRules)
|
|
.where(eq(bufferTimeRules.serviceId, serviceId));
|
|
|
|
// Priority 1: exact match (serviceId + sizeCategory + coatType all match)
|
|
const exact = allRules.find(
|
|
(r) =>
|
|
r.sizeCategory === sizeCategory &&
|
|
r.coatType === coatType
|
|
);
|
|
if (exact) return exact.bufferMinutes;
|
|
|
|
// Priority 2: service + size, null coatType
|
|
const serviceSize = allRules.find(
|
|
(r) =>
|
|
r.sizeCategory === sizeCategory &&
|
|
r.coatType === null
|
|
);
|
|
if (serviceSize) return serviceSize.bufferMinutes;
|
|
|
|
// Priority 3: service + coat, null sizeCategory
|
|
const serviceCoat = allRules.find(
|
|
(r) =>
|
|
r.sizeCategory === null &&
|
|
r.coatType === coatType
|
|
);
|
|
if (serviceCoat) return serviceCoat.bufferMinutes;
|
|
|
|
// Priority 4: service only (null sizeCategory, null coatType)
|
|
const serviceOnly = allRules.find(
|
|
(r) =>
|
|
r.sizeCategory === null &&
|
|
r.coatType === null
|
|
);
|
|
if (serviceOnly) return serviceOnly.bufferMinutes;
|
|
|
|
// Priority 5: fallback to service.defaultBufferMinutes
|
|
const [service] = await db
|
|
.select({ defaultBufferMinutes: services.defaultBufferMinutes })
|
|
.from(services)
|
|
.where(eq(services.id, serviceId))
|
|
.limit(1);
|
|
|
|
if (service?.defaultBufferMinutes != null) {
|
|
return service.defaultBufferMinutes;
|
|
}
|
|
|
|
// Priority 6: final fallback to 0
|
|
return 0;
|
|
} |