Merge branch 'develop' into MED-97
This commit is contained in:
43
app/home/(user)/_lib/server/actions.ts
Normal file
43
app/home/(user)/_lib/server/actions.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
'use server';
|
||||
|
||||
import { updateLineItem } from '@lib/data/cart';
|
||||
import { StoreProductVariant } from '@medusajs/types';
|
||||
|
||||
import { handleAddToCart } from '~/lib/services/medusaCart.service';
|
||||
import { createInitialReservation } from '~/lib/services/reservation.service';
|
||||
|
||||
export async function createInitialReservationAction(
|
||||
selectedVariant: Pick<StoreProductVariant, 'id'>,
|
||||
countryCode: string,
|
||||
serviceId: number,
|
||||
clinicId: number,
|
||||
appointmentUserId: number,
|
||||
syncUserId: number,
|
||||
startTime: Date,
|
||||
locationId: number | null,
|
||||
comments?: string,
|
||||
) {
|
||||
const { addedItem } = await handleAddToCart({
|
||||
selectedVariant,
|
||||
countryCode,
|
||||
});
|
||||
|
||||
if (addedItem) {
|
||||
const reservation = await createInitialReservation({
|
||||
serviceId,
|
||||
clinicId,
|
||||
appointmentUserId,
|
||||
syncUserID: syncUserId,
|
||||
startTime,
|
||||
medusaLineItemId: addedItem.id,
|
||||
locationId,
|
||||
comments,
|
||||
});
|
||||
|
||||
await updateLineItem({
|
||||
lineId: addedItem.id,
|
||||
quantity: addedItem.quantity,
|
||||
metadata: { connectedOnlineReservationId: reservation.id },
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,9 @@ import { AccountWithParams } from "@/packages/features/accounts/src/types/accoun
|
||||
import { createI18nServerInstance } from "~/lib/i18n/i18n.server";
|
||||
import { getSupabaseServerAdminClient } from "@/packages/supabase/src/clients/server-admin-client";
|
||||
import { createNotificationsApi } from "@/packages/features/notifications/src/server/api";
|
||||
import { FailureReason } from '~/lib/types/connected-online';
|
||||
import { getOrderedTtoServices } from '~/lib/services/reservation.service';
|
||||
import { bookAppointment } from '~/lib/services/connected-online.service';
|
||||
|
||||
const ANALYSIS_PACKAGES_TYPE_HANDLE = 'analysis-packages';
|
||||
const ANALYSIS_TYPE_HANDLE = 'synlab-analysis';
|
||||
@@ -77,14 +80,14 @@ export const initiatePayment = async ({
|
||||
if (!montonioPaymentSessionId) {
|
||||
throw new Error('Montonio payment session ID is missing');
|
||||
}
|
||||
const url = await handleNavigateToPayment({
|
||||
const props = await handleNavigateToPayment({
|
||||
language,
|
||||
paymentSessionId: montonioPaymentSessionId,
|
||||
amount: totalByMontonio,
|
||||
currencyCode: cart.currency_code,
|
||||
cartId: cart.id,
|
||||
});
|
||||
return { url };
|
||||
return { ...props, isFullyPaidByBenefits };
|
||||
} else {
|
||||
// place order if all paid already
|
||||
const { orderId } = await handlePlaceOrder({ cart });
|
||||
@@ -109,13 +112,13 @@ export const initiatePayment = async ({
|
||||
if (!webhookResponse.ok) {
|
||||
throw new Error('Failed to send company benefits webhook');
|
||||
}
|
||||
return { isFullyPaidByBenefits, orderId };
|
||||
return { isFullyPaidByBenefits, orderId, unavailableLineItemIds: [] };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error initiating payment', error);
|
||||
}
|
||||
|
||||
return { url: null }
|
||||
return { url: null, isFullyPaidByBenefits: false, orderId: null, unavailableLineItemIds: [] };
|
||||
}
|
||||
|
||||
export async function handlePlaceOrder({
|
||||
@@ -136,6 +139,8 @@ export async function handlePlaceOrder({
|
||||
medusaOrder,
|
||||
});
|
||||
|
||||
const orderContainsSynlabItems = !!orderedAnalysisElements?.length;
|
||||
|
||||
try {
|
||||
const existingAnalysisOrder = await getAnalysisOrder({
|
||||
medusaOrderId: medusaOrder.id,
|
||||
@@ -148,15 +153,38 @@ export async function handlePlaceOrder({
|
||||
// ignored
|
||||
}
|
||||
|
||||
const orderId = await createAnalysisOrder({
|
||||
medusaOrder,
|
||||
orderedAnalysisElements,
|
||||
});
|
||||
let orderId: number | undefined = undefined;
|
||||
if (orderContainsSynlabItems) {
|
||||
orderId = await createAnalysisOrder({
|
||||
medusaOrder,
|
||||
orderedAnalysisElements,
|
||||
});
|
||||
}
|
||||
|
||||
const orderResult = await getOrderResultParameters(medusaOrder);
|
||||
|
||||
const { medusaOrderId, email, analysisPackageOrder, analysisItemsOrder } =
|
||||
orderResult;
|
||||
|
||||
const orderedTtoServices = await getOrderedTtoServices({ medusaOrder });
|
||||
let bookServiceResults: {
|
||||
success: boolean;
|
||||
reason?: FailureReason;
|
||||
serviceId?: number;
|
||||
}[] = [];
|
||||
if (orderedTtoServices?.length) {
|
||||
const bookingPromises = orderedTtoServices.map((service) =>
|
||||
bookAppointment(
|
||||
service.service_id,
|
||||
service.clinic_id,
|
||||
service.service_user_id,
|
||||
service.sync_user_id,
|
||||
service.start_time,
|
||||
),
|
||||
);
|
||||
bookServiceResults = await Promise.all(bookingPromises);
|
||||
}
|
||||
|
||||
if (email) {
|
||||
if (analysisPackageOrder) {
|
||||
await sendAnalysisPackageOrderEmail({
|
||||
@@ -184,6 +212,17 @@ export async function handlePlaceOrder({
|
||||
await sendOrderToMedipost({ medusaOrderId, orderedAnalysisElements });
|
||||
}
|
||||
|
||||
if (bookServiceResults.some(({ success }) => success === false)) {
|
||||
const failedServiceBookings = bookServiceResults.filter(
|
||||
({ success }) => success === false,
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
failedServiceBookings,
|
||||
orderId,
|
||||
};
|
||||
}
|
||||
|
||||
return { success: true, orderId };
|
||||
} catch (error) {
|
||||
console.error('Failed to place order', error);
|
||||
|
||||
13
app/home/(user)/_lib/server/is-valid-open-ai-env.ts
Normal file
13
app/home/(user)/_lib/server/is-valid-open-ai-env.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import OpenAI from 'openai';
|
||||
|
||||
export const isValidOpenAiEnv = async () => {
|
||||
const client = new OpenAI();
|
||||
|
||||
try {
|
||||
await client.models.list();
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.log('No openAI env');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -45,10 +45,6 @@ async function analysesLoader() {
|
||||
})
|
||||
: null;
|
||||
|
||||
const serviceCategories = productCategories.filter(
|
||||
({ parent_category }) => parent_category?.handle === 'tto-categories',
|
||||
);
|
||||
|
||||
return {
|
||||
analyses:
|
||||
categoryProducts?.response.products
|
||||
|
||||
@@ -1,20 +1,30 @@
|
||||
import { cache } from 'react';
|
||||
|
||||
import { getProductCategories } from '@lib/data';
|
||||
import { getProductCategories, listProducts } from '@lib/data';
|
||||
|
||||
import { ServiceCategory } from '../../_components/service-categories';
|
||||
|
||||
async function categoryLoader({
|
||||
handle,
|
||||
}: {
|
||||
handle: string;
|
||||
}): Promise<{ category: ServiceCategory | null }> {
|
||||
const response = await getProductCategories({
|
||||
handle,
|
||||
fields: '*products, is_active, metadata',
|
||||
});
|
||||
import { loadCountryCodes } from './load-analyses';
|
||||
|
||||
async function categoryLoader({ handle }: { handle: string }) {
|
||||
const [response, countryCodes] = await Promise.all([
|
||||
getProductCategories({
|
||||
handle,
|
||||
limit: 1,
|
||||
}),
|
||||
loadCountryCodes(),
|
||||
]);
|
||||
const category = response.product_categories[0];
|
||||
const countryCode = countryCodes[0]!;
|
||||
|
||||
if (!response.product_categories?.[0]?.id) {
|
||||
return { category: null };
|
||||
}
|
||||
|
||||
const {
|
||||
response: { products: categoryProducts },
|
||||
} = await listProducts({
|
||||
countryCode,
|
||||
queryParams: { limit: 100, category_id: response.product_categories[0].id },
|
||||
});
|
||||
|
||||
return {
|
||||
category: {
|
||||
@@ -25,6 +35,8 @@ async function categoryLoader({
|
||||
description: category?.description || '',
|
||||
handle: category?.handle || '',
|
||||
name: category?.name || '',
|
||||
countryCode,
|
||||
products: categoryProducts,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,38 +10,36 @@ async function ttoServicesLoader() {
|
||||
});
|
||||
|
||||
const heroCategories = response.product_categories?.filter(
|
||||
({ parent_category, is_active, metadata }) =>
|
||||
parent_category?.handle === 'tto-categories' &&
|
||||
is_active &&
|
||||
metadata?.isHero,
|
||||
({ parent_category, metadata }) =>
|
||||
parent_category?.handle === 'tto-categories' && metadata?.isHero,
|
||||
);
|
||||
|
||||
const ttoCategories = response.product_categories?.filter(
|
||||
({ parent_category, is_active, metadata }) =>
|
||||
parent_category?.handle === 'tto-categories' &&
|
||||
is_active &&
|
||||
!metadata?.isHero,
|
||||
({ parent_category, metadata }) =>
|
||||
parent_category?.handle === 'tto-categories' && !metadata?.isHero,
|
||||
);
|
||||
|
||||
return {
|
||||
heroCategories:
|
||||
heroCategories.map<ServiceCategory>(
|
||||
({ name, handle, metadata, description }) => ({
|
||||
heroCategories.map<Omit<ServiceCategory, 'countryCode'>>(
|
||||
({ name, handle, metadata, description, products }) => ({
|
||||
name,
|
||||
handle,
|
||||
color:
|
||||
typeof metadata?.color === 'string' ? metadata.color : 'primary',
|
||||
description,
|
||||
products: products ?? [],
|
||||
}),
|
||||
) ?? [],
|
||||
ttoCategories:
|
||||
ttoCategories.map<ServiceCategory>(
|
||||
({ name, handle, metadata, description }) => ({
|
||||
ttoCategories.map<Omit<ServiceCategory, 'countryCode'>>(
|
||||
({ name, handle, metadata, description, products }) => ({
|
||||
name,
|
||||
handle,
|
||||
color:
|
||||
typeof metadata?.color === 'string' ? metadata.color : 'primary',
|
||||
description,
|
||||
products: products ?? [],
|
||||
}),
|
||||
) ?? [],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user