180 lines
5.3 KiB
TypeScript
180 lines
5.3 KiB
TypeScript
import type { BusinessService, BusinessServiceClass } from '@/api/types';
|
||
import { formatDuration, formatPriceToman } from '@/utils/format';
|
||
|
||
export function activeServiceClasses(classes: BusinessServiceClass[]): BusinessServiceClass[] {
|
||
return [...classes]
|
||
.filter((item) => item.isActive)
|
||
.sort((a, b) => a.sortOrder - b.sortOrder || a.title.localeCompare(b.title, 'fa'));
|
||
}
|
||
|
||
export function usesServiceClasses(
|
||
classesEnabled: boolean,
|
||
classes: BusinessServiceClass[],
|
||
): boolean {
|
||
return classesEnabled && activeServiceClasses(classes).length > 0;
|
||
}
|
||
|
||
export function getClassPriceForService(
|
||
service: BusinessService,
|
||
classId: string,
|
||
) {
|
||
return service.classPrices?.find(
|
||
(item) => item.classId === classId && item.isActive,
|
||
);
|
||
}
|
||
|
||
export function serviceHasActiveClassPrices(
|
||
service: BusinessService,
|
||
classes: BusinessServiceClass[],
|
||
): boolean {
|
||
const activeIds = new Set(activeServiceClasses(classes).map((item) => item.id));
|
||
return (service.classPrices ?? []).some(
|
||
(item) => item.isActive && activeIds.has(item.classId),
|
||
);
|
||
}
|
||
|
||
export function isServiceBookable(
|
||
service: BusinessService,
|
||
classesEnabled: boolean,
|
||
classes: BusinessServiceClass[],
|
||
): boolean {
|
||
if (!service.isActive) return false;
|
||
if (!usesServiceClasses(classesEnabled, classes)) return true;
|
||
return serviceHasActiveClassPrices(service, classes);
|
||
}
|
||
|
||
export function servicePriceLabel(
|
||
service: BusinessService,
|
||
classesEnabled: boolean,
|
||
classes: BusinessServiceClass[],
|
||
): string {
|
||
if (!usesServiceClasses(classesEnabled, classes)) {
|
||
return `${formatPriceToman(service.price)} تومان`;
|
||
}
|
||
|
||
const activeClasses = activeServiceClasses(classes);
|
||
const prices = activeClasses
|
||
.map((item) => getClassPriceForService(service, item.id))
|
||
.filter(Boolean)
|
||
.map((item) => Number.parseFloat(item!.price));
|
||
|
||
if (!prices.length) return 'قیمت تعیین نشده';
|
||
const min = Math.min(...prices);
|
||
const max = Math.max(...prices);
|
||
if (min === max) {
|
||
return `${formatPriceToman(String(min))} تومان`;
|
||
}
|
||
return `از ${formatPriceToman(String(min))} تومان`;
|
||
}
|
||
|
||
export function serviceDurationLabel(
|
||
service: BusinessService,
|
||
classesEnabled: boolean,
|
||
classes: BusinessServiceClass[],
|
||
classId?: string | null,
|
||
): string {
|
||
if (classId) {
|
||
const classPrice = getClassPriceForService(service, classId);
|
||
return formatDuration(
|
||
classPrice?.durationMinutes ?? service.durationMinutes,
|
||
service.serviceTemplate?.defaultDurationMinutes,
|
||
);
|
||
}
|
||
|
||
if (!usesServiceClasses(classesEnabled, classes)) {
|
||
return formatDuration(
|
||
service.durationMinutes,
|
||
service.serviceTemplate?.defaultDurationMinutes,
|
||
);
|
||
}
|
||
|
||
const activeClasses = activeServiceClasses(classes);
|
||
const durations = activeClasses
|
||
.map((item) => getClassPriceForService(service, item.id))
|
||
.filter(Boolean)
|
||
.map((item) =>
|
||
item!.durationMinutes ?? service.durationMinutes ?? service.serviceTemplate?.defaultDurationMinutes,
|
||
)
|
||
.filter((value): value is number => value != null && value > 0);
|
||
|
||
if (!durations.length) {
|
||
return formatDuration(
|
||
service.durationMinutes,
|
||
service.serviceTemplate?.defaultDurationMinutes,
|
||
);
|
||
}
|
||
|
||
const min = Math.min(...durations);
|
||
const max = Math.max(...durations);
|
||
if (min === max) return `${min} دقیقه`;
|
||
return `${min}–${max} دقیقه`;
|
||
}
|
||
|
||
export function resolvedServicePrice(
|
||
service: BusinessService,
|
||
classId: string,
|
||
): string | null {
|
||
const classPrice = getClassPriceForService(service, classId);
|
||
return classPrice?.price ?? null;
|
||
}
|
||
|
||
export function resolveServiceDurationMinutes(
|
||
service: BusinessService,
|
||
classesEnabled: boolean,
|
||
classes: BusinessServiceClass[],
|
||
classId?: string | null,
|
||
): number {
|
||
if (classId) {
|
||
const classPrice = getClassPriceForService(service, classId);
|
||
const fromClass =
|
||
classPrice?.durationMinutes ?? service.durationMinutes ?? service.serviceTemplate?.defaultDurationMinutes;
|
||
if (fromClass != null && fromClass > 0) return fromClass;
|
||
}
|
||
|
||
if (usesServiceClasses(classesEnabled, classes) && classId) {
|
||
const classPrice = getClassPriceForService(service, classId);
|
||
const value =
|
||
classPrice?.durationMinutes ??
|
||
service.durationMinutes ??
|
||
service.serviceTemplate?.defaultDurationMinutes;
|
||
if (value != null && value > 0) return value;
|
||
}
|
||
|
||
const fallback =
|
||
service.durationMinutes ?? service.serviceTemplate?.defaultDurationMinutes;
|
||
return fallback != null && fallback > 0 ? fallback : 30;
|
||
}
|
||
|
||
export function totalDurationForServices(
|
||
services: BusinessService[],
|
||
classesEnabled: boolean,
|
||
classes: BusinessServiceClass[],
|
||
classId?: string | null,
|
||
): number {
|
||
return services.reduce(
|
||
(sum, service) =>
|
||
sum + resolveServiceDurationMinutes(service, classesEnabled, classes, classId),
|
||
0,
|
||
);
|
||
}
|
||
|
||
export function totalPriceForServices(
|
||
services: BusinessService[],
|
||
classesEnabled: boolean,
|
||
classes: BusinessServiceClass[],
|
||
classId?: string | null,
|
||
): number | null {
|
||
if (usesServiceClasses(classesEnabled, classes)) {
|
||
if (!classId) return null;
|
||
let total = 0;
|
||
for (const service of services) {
|
||
const price = resolvedServicePrice(service, classId);
|
||
if (!price) return null;
|
||
total += Number.parseFloat(price);
|
||
}
|
||
return total;
|
||
}
|
||
|
||
return services.reduce((sum, service) => sum + Number.parseFloat(service.price), 0);
|
||
}
|