344 lines
9.1 KiB
TypeScript
344 lines
9.1 KiB
TypeScript
'use server';
|
|
|
|
import { revalidatePath } from 'next/cache';
|
|
|
|
import { listRegions } from '@lib/data/regions';
|
|
import { StoreCart, StoreOrder } from '@medusajs/types';
|
|
|
|
import { getLogger } from '@kit/shared/logger';
|
|
import { Tables } from '@kit/supabase/database';
|
|
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
|
|
|
import { EnrichedCartItem } from '../../app/home/(user)/_components/cart/types';
|
|
import { loadCurrentUserAccount } from '../../app/home/(user)/_lib/server/load-user-account';
|
|
import { createCartEntriesLog } from './audit/cartEntries';
|
|
import { handleDeleteCartItem } from './medusaCart.service';
|
|
|
|
type Locations = Tables<{ schema: 'medreport' }, 'connected_online_locations'>;
|
|
type Services = Tables<{ schema: 'medreport' }, 'connected_online_services'>;
|
|
type ServiceProviders = Tables<
|
|
{ schema: 'medreport' },
|
|
'connected_online_service_providers'
|
|
>;
|
|
|
|
export async function getCartReservations(
|
|
medusaCart: StoreCart,
|
|
): Promise<EnrichedCartItem[]> {
|
|
const supabase = getSupabaseServerClient();
|
|
|
|
const cartLineItemIds = medusaCart.items?.map(({ id }) => id);
|
|
|
|
if (!cartLineItemIds?.length) {
|
|
return [];
|
|
}
|
|
|
|
const { data: reservations } = await supabase
|
|
.schema('medreport')
|
|
.from('connected_online_reservation')
|
|
.select(
|
|
'id, startTime:start_time, service:service_id, location:location_sync_id, serviceProvider:service_user_id, medusaCartLineItemId:medusa_cart_line_item_id',
|
|
)
|
|
.in('medusa_cart_line_item_id', cartLineItemIds)
|
|
.throwOnError();
|
|
|
|
const locationSyncIds: number[] =
|
|
reservations
|
|
?.filter((reservation) => !!reservation.location)
|
|
.map((reservation) => reservation.location!) ?? [];
|
|
const serviceIds =
|
|
reservations?.map((reservation) => reservation.service) ?? [];
|
|
const serviceProviderIds =
|
|
reservations.map((reservation) => reservation.serviceProvider) ?? [];
|
|
|
|
let locations:
|
|
| {
|
|
syncId: Locations['sync_id'];
|
|
name: Locations['name'];
|
|
address: Locations['address'];
|
|
}[]
|
|
| null = null;
|
|
if (locationSyncIds.length) {
|
|
({ data: locations } = await supabase
|
|
.schema('medreport')
|
|
.from('connected_online_locations')
|
|
.select('syncId:sync_id, name, address')
|
|
.in('sync_id', locationSyncIds)
|
|
.throwOnError());
|
|
}
|
|
|
|
let services:
|
|
| {
|
|
id: Services['id'];
|
|
name: Services['name'];
|
|
}[]
|
|
| null = null;
|
|
if (serviceIds.length) {
|
|
({ data: services } = await supabase
|
|
.schema('medreport')
|
|
.from('connected_online_services')
|
|
.select('name, id')
|
|
.in('id', serviceIds)
|
|
.throwOnError());
|
|
}
|
|
|
|
let serviceProviders:
|
|
| {
|
|
id: ServiceProviders['id'];
|
|
name: ServiceProviders['name'];
|
|
jobTitleEt: ServiceProviders['job_title_et'];
|
|
jobTitleEn: ServiceProviders['job_title_en'];
|
|
jobTitleRu: ServiceProviders['job_title_ru'];
|
|
spokenLanguages: ServiceProviders['spoken_languages'];
|
|
}[]
|
|
| null = null;
|
|
if (serviceProviderIds.length) {
|
|
({ data: serviceProviders } = await supabase
|
|
.schema('medreport')
|
|
.from('connected_online_service_providers')
|
|
.select(
|
|
'id, name, jobTitleEt:job_title_et, jobTitleEn:job_title_en, jobTitleRu:job_title_ru, spokenLanguages:spoken_languages',
|
|
)
|
|
.in('id', serviceProviderIds)
|
|
.throwOnError());
|
|
}
|
|
|
|
const results = [];
|
|
for (const reservation of reservations) {
|
|
if (reservation.medusaCartLineItemId === null) {
|
|
continue;
|
|
}
|
|
|
|
const cartLineItem = medusaCart.items?.find(
|
|
(item) => item.id === reservation.medusaCartLineItemId,
|
|
);
|
|
|
|
if (!cartLineItem) {
|
|
continue;
|
|
}
|
|
|
|
const location = locations?.find(
|
|
(location) => location.syncId === reservation.location,
|
|
);
|
|
const service = services?.find(
|
|
(service) => service.id === reservation.service,
|
|
);
|
|
const serviceProvider = serviceProviders?.find(
|
|
(serviceProvider) => serviceProvider.id === reservation.serviceProvider,
|
|
);
|
|
|
|
const countryCodes = await listRegions().then((regions) =>
|
|
regions?.map((r) => r.countries?.map((c) => c.iso_2)).flat(),
|
|
);
|
|
|
|
const enrichedReservation = {
|
|
...reservation,
|
|
location,
|
|
service,
|
|
serviceProvider,
|
|
};
|
|
|
|
results.push({
|
|
...cartLineItem,
|
|
reservation: {
|
|
...enrichedReservation,
|
|
medusaCartLineItemId: reservation.medusaCartLineItemId!,
|
|
countryCode: countryCodes[0],
|
|
},
|
|
});
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
export async function createInitialReservation({
|
|
serviceId,
|
|
clinicId,
|
|
appointmentUserId,
|
|
syncUserID,
|
|
startTime,
|
|
medusaLineItemId,
|
|
locationId,
|
|
comments = '',
|
|
}: {
|
|
serviceId: number;
|
|
clinicId: number;
|
|
appointmentUserId: number;
|
|
syncUserID: number;
|
|
startTime: Date;
|
|
medusaLineItemId: string;
|
|
locationId?: number | null;
|
|
comments?: string;
|
|
}) {
|
|
const logger = await getLogger();
|
|
const supabase = getSupabaseServerClient();
|
|
|
|
const {
|
|
data: { user },
|
|
} = await supabase.auth.getUser();
|
|
|
|
const userId = user?.id;
|
|
|
|
if (!userId) {
|
|
throw new Error('User not authenticated');
|
|
}
|
|
|
|
logger.info(
|
|
'Creating reservation' +
|
|
JSON.stringify({ serviceId, clinicId, startTime, userId }),
|
|
);
|
|
|
|
try {
|
|
const { data: createdReservation } = await supabase
|
|
.schema('medreport')
|
|
.from('connected_online_reservation')
|
|
.insert({
|
|
clinic_id: clinicId,
|
|
comments,
|
|
lang: 'et',
|
|
service_id: serviceId,
|
|
service_user_id: appointmentUserId,
|
|
start_time: startTime.toString(),
|
|
sync_user_id: syncUserID,
|
|
user_id: userId,
|
|
status: 'PENDING',
|
|
medusa_cart_line_item_id: medusaLineItemId,
|
|
location_sync_id: locationId,
|
|
})
|
|
.select('id')
|
|
.single()
|
|
.throwOnError();
|
|
|
|
logger.info(
|
|
`Created reservation ${JSON.stringify({ createdReservation, userId })}`,
|
|
);
|
|
|
|
return createdReservation;
|
|
} catch (e) {
|
|
logger.error(
|
|
`Failed to create initial reservation ${JSON.stringify({ serviceId, clinicId, startTime })} ${e}`,
|
|
);
|
|
await handleDeleteCartItem({ lineId: medusaLineItemId });
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
export async function cancelReservation(medusaLineItemId: string) {
|
|
const supabase = getSupabaseServerClient();
|
|
|
|
return supabase
|
|
.schema('medreport')
|
|
.from('connected_online_reservation')
|
|
.update({
|
|
status: 'CANCELLED',
|
|
})
|
|
.eq('medusa_cart_line_item_id', medusaLineItemId)
|
|
.throwOnError();
|
|
}
|
|
|
|
export async function getOrderedTtoServices({
|
|
cart,
|
|
medusaOrder,
|
|
}: {
|
|
cart?: StoreCart;
|
|
medusaOrder?: StoreOrder;
|
|
}) {
|
|
const supabase = getSupabaseServerClient();
|
|
|
|
if (!medusaOrder && !cart) {
|
|
throw new Error('No cart or medusa order provided');
|
|
}
|
|
|
|
const ttoReservationIds: number[] =
|
|
(medusaOrder?.items ?? cart?.items)
|
|
?.filter(({ metadata }) => !!metadata?.connectedOnlineReservationId)
|
|
.map(({ metadata }) => Number(metadata!.connectedOnlineReservationId)) ??
|
|
[];
|
|
|
|
const { data: orderedTtoServices } = await supabase
|
|
.schema('medreport')
|
|
.from('connected_online_reservation')
|
|
.select('*, provider:connected_online_providers(key)')
|
|
.in('id', ttoReservationIds)
|
|
.throwOnError();
|
|
|
|
return orderedTtoServices;
|
|
}
|
|
|
|
export async function updateReservationTime({
|
|
reservationId,
|
|
newStartTime,
|
|
newServiceId,
|
|
newAppointmentUserId,
|
|
newSyncUserId,
|
|
newLocationId, // TODO stop allowing null when Connected starts returning the correct ids instead of -1
|
|
cartId,
|
|
}: {
|
|
reservationId: number;
|
|
newStartTime: Date;
|
|
newServiceId: number;
|
|
newAppointmentUserId: number;
|
|
newSyncUserId: number;
|
|
newLocationId: number | null;
|
|
cartId: string;
|
|
}) {
|
|
const logger = await getLogger();
|
|
const supabase = getSupabaseServerClient();
|
|
|
|
const {
|
|
data: { user },
|
|
} = await supabase.auth.getUser();
|
|
|
|
const userId = user?.id;
|
|
const { account } = await loadCurrentUserAccount();
|
|
|
|
if (!userId || !account) {
|
|
throw new Error('User not authenticated');
|
|
}
|
|
|
|
const reservationData = JSON.stringify({
|
|
reservationId,
|
|
newStartTime,
|
|
newServiceId,
|
|
newAppointmentUserId,
|
|
newSyncUserId,
|
|
newLocationId,
|
|
userId,
|
|
cartId,
|
|
});
|
|
|
|
logger.info('Updating reservation' + reservationData);
|
|
try {
|
|
await supabase
|
|
.schema('medreport')
|
|
.from('connected_online_reservation')
|
|
.update({
|
|
service_id: newServiceId,
|
|
service_user_id: newAppointmentUserId,
|
|
sync_user_id: newSyncUserId,
|
|
start_time: newStartTime.toString(),
|
|
location_sync_id: newLocationId,
|
|
})
|
|
.eq('id', reservationId)
|
|
.eq('user_id', user.id)
|
|
.throwOnError();
|
|
|
|
logger.info(`Successfully updated reservation ${reservationData}`);
|
|
await createCartEntriesLog({
|
|
operation: 'CHANGE_RESERVATION',
|
|
accountId: account.id,
|
|
cartId: cartId,
|
|
comment: `${reservationData}`,
|
|
});
|
|
revalidatePath('/home/cart', 'layout');
|
|
} catch (e) {
|
|
logger.error(`Failed to update reservation ${reservationData}`);
|
|
await createCartEntriesLog({
|
|
operation: 'CHANGE_RESERVATION',
|
|
accountId: account.id,
|
|
cartId: cartId,
|
|
comment: `${e}`,
|
|
});
|
|
throw e;
|
|
}
|
|
}
|