176 lines
5.0 KiB
TypeScript

import { getClientWithToken } from '../apollo/client';
import * as GQL from '../types';
import { BaseService } from './base';
import { ServicesService } from './services';
import { type VariablesOf } from '@graphql-typed-document-node/core';
import { getMinutes } from '@repo/utils/datetime-format';
import dayjs from 'dayjs';
const ERRORS = {
HAS_ORDERS: 'Slot has orders',
MISSING_DATE: 'Missing date',
MISSING_SERVICE: 'Missing service',
MISSING_SERVICE_ID: 'Missing service id',
NO_PERMISSION: 'No permission',
};
export class SlotsService extends BaseService {
async createSlot(variables: VariablesOf<typeof GQL.CreateSlotDocument>) {
const { customer } = await this._getUser();
const { mutate } = await getClientWithToken();
const mutationResult = await mutate({
mutation: GQL.CreateSlotDocument,
variables: {
...variables,
input: {
...variables.input,
master: customer?.documentId,
},
},
});
const error = mutationResult.errors?.at(0);
if (error) throw new Error(error.message);
return mutationResult.data;
}
async deleteSlot(variables: VariablesOf<typeof GQL.DeleteSlotDocument>) {
await this.checkPermission(variables);
const { slot } = await this.getSlot({ documentId: variables.documentId });
if (slot?.orders?.length) {
throw new Error(ERRORS.HAS_ORDERS);
}
const { mutate } = await getClientWithToken();
const mutationResult = await mutate({
mutation: GQL.DeleteSlotDocument,
variables,
});
const error = mutationResult.errors?.at(0);
if (error) throw new Error(error.message);
return mutationResult.data;
}
async getAvailableTimeSlots(
variables: VariablesOf<typeof GQL.GetSlotsDocument>,
context: { service: GQL.ServiceFiltersInput },
) {
if (!variables.filters?.datetime_start) throw new Error(ERRORS.MISSING_DATE);
if (!context?.service?.documentId?.eq) throw new Error(ERRORS.MISSING_SERVICE_ID);
const { query } = await getClientWithToken();
const getSlotsResult = await query({
query: GQL.GetSlotsOrdersDocument,
variables: {
filters: {
...variables.filters,
state: { eq: GQL.Enum_Slot_State.Open },
},
},
});
if (getSlotsResult.error) throw new Error(getSlotsResult.error.message);
const servicesService = new ServicesService(this._user);
const { service } = await servicesService.getService({
documentId: context.service.documentId.eq,
});
if (!service) throw new Error(ERRORS.MISSING_SERVICE);
const serviceDuration = getMinutes(service.duration);
const slots = getSlotsResult.data.slots;
const times: Array<{ slotId: string; time: string }> = [];
const now = dayjs();
for (const slot of slots) {
if (!slot?.datetime_start || !slot?.datetime_end) continue;
let datetimeStart = dayjs(slot.datetime_start);
const datetimeEnd = dayjs(slot.datetime_end).subtract(serviceDuration, 'minutes');
while (datetimeStart.valueOf() <= datetimeEnd.valueOf()) {
const slotStartTime = datetimeStart;
const potentialDatetimeEnd = datetimeStart.add(serviceDuration, 'minutes');
const hasConflict = slot.orders.some(
(order) =>
order?.state &&
![GQL.Enum_Order_State.Cancelled].includes(order.state) &&
slotStartTime.isBefore(dayjs(order.datetime_end)) &&
potentialDatetimeEnd.isAfter(dayjs(order.datetime_start)),
);
if (!hasConflict && datetimeStart.isAfter(now, 'minute')) {
times.push({ slotId: slot.documentId, time: datetimeStart.toISOString() });
}
datetimeStart = datetimeStart.add(15, 'minutes');
}
}
return { slots, times };
}
async getSlot(variables: VariablesOf<typeof GQL.GetSlotDocument>) {
const { query } = await getClientWithToken();
const result = await query({
query: GQL.GetSlotDocument,
variables,
});
return result.data;
}
async getSlots(variables: VariablesOf<typeof GQL.GetSlotsDocument>) {
const { query } = await getClientWithToken();
const result = await query({
query: GQL.GetSlotsDocument,
variables,
});
return result.data;
}
async updateSlot(variables: VariablesOf<typeof GQL.UpdateSlotDocument>) {
await this.checkPermission(variables);
const { mutate } = await getClientWithToken();
const mutationResult = await mutate({
mutation: GQL.UpdateSlotDocument,
variables,
});
const error = mutationResult.errors?.at(0);
if (error) throw new Error(error.message);
return mutationResult.data;
}
private async checkPermission(
variables: Pick<VariablesOf<typeof GQL.GetSlotDocument>, 'documentId'>,
) {
const { customer } = await this._getUser();
const { slot } = await this.getSlot({ documentId: variables.documentId });
if (slot?.master?.documentId !== customer?.documentId) throw new Error(ERRORS.NO_PERMISSION);
}
}