Files
BarevClub/packages/services/src/catalog.ts
T
2026-10-17 19:45:51 +04:00

73 lines
2.3 KiB
TypeScript

import {
fromPrice,
paramsSchemaSchema,
pricingSchema,
slotConfigSchema,
type CategoryCode,
type ParamsSchema,
type Pricing,
type SlotConfig,
} from '@barev/core';
import type { Db, Service, Tx } from '@barev/db';
import { DomainError } from './context.ts';
export interface ParsedService {
id: string;
code: string;
category: CategoryCode;
categoryDiscountPct: number;
flow: Service['flow'];
providerRole: Service['providerRole'];
pricing: Pricing;
paramsSchema: ParamsSchema;
slotConfig: SlotConfig;
lateCancelFeePct: number;
isOnline: boolean;
active: boolean;
fromPrice: number;
}
type ServiceWithCategory = Service & { category: { code: CategoryCode; barevPlusDiscountPct: number } };
export function parseService(s: ServiceWithCategory): ParsedService {
const pricing = pricingSchema.parse(s.pricing);
return {
id: s.id,
code: s.code,
category: s.category.code,
categoryDiscountPct: s.category.barevPlusDiscountPct,
flow: s.flow,
providerRole: s.providerRole,
pricing,
paramsSchema: paramsSchemaSchema.parse(s.paramsSchema),
slotConfig: slotConfigSchema.parse(s.slotConfig),
lateCancelFeePct: s.lateCancelFeePct,
isOnline: s.isOnline,
active: s.active,
fromPrice: fromPrice(pricing),
};
}
/** Каталог для лендинга и Mini App: все направления, у услуг — «от N ֏» и признак доступности */
export async function listCatalog(db: Db) {
const categories = await db.category.findMany({
orderBy: { sortOrder: 'asc' },
include: { services: { orderBy: { sortOrder: 'asc' }, include: { category: true } } },
});
return categories.map((c) => ({
code: c.code,
barevPlusDiscountPct: c.barevPlusDiscountPct,
services: c.services.map((s) => {
const p = parseService(s);
return { code: p.code, flow: p.flow, fromPrice: p.fromPrice, active: p.active, isOnline: p.isOnline };
}),
}));
}
export async function getServiceByCode(db: Db | Tx, code: string, opts: { activeOnly?: boolean } = {}): Promise<ParsedService> {
const s = await db.service.findUnique({ where: { code }, include: { category: true } });
if (!s) throw new DomainError('NOT_FOUND');
if (opts.activeOnly && !s.active) throw new DomainError('SERVICE_UNAVAILABLE');
return parseService(s);
}