feat: create email template for TTO reservation confirmation

feat: implement order notifications service with TTO reservation confirmation handling

feat: create migration for TTO booking email webhook trigger
This commit is contained in:
Danel Kungla
2025-09-30 16:05:43 +03:00
parent 4003284f3a
commit 72f6f2b716
56 changed files with 3692 additions and 294 deletions

View File

@@ -45,7 +45,6 @@ export const BookingProvider: React.FC<{
const updateTimeSlots = async (serviceIds: number[]) => {
setIsLoadingTimeSlots(true);
try {
console.log('serviceIds', serviceIds, selectedLocationId);
const response = await getAvailableTimeSlotsForDisplay(
serviceIds,
selectedLocationId,

View File

@@ -167,7 +167,7 @@ const TimeSlots = ({
return handleBookTime(timeSlot);
};
console.log('paginatedBookings', booking.isLoadingTimeSlots);
return (
<Skeleton
isLoading={booking.isLoadingTimeSlots}

View File

@@ -2,22 +2,23 @@
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { formatCurrency } from '@/packages/shared/src/utils';
import { StoreCart, StoreCartLineItem } from '@medusajs/types';
import { Loader2 } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { AccountBalanceSummary } from '@kit/accounts/services/account-balance.service';
import { Button } from '@kit/ui/button';
import { Card, CardContent, CardHeader } from '@kit/ui/card';
import { Trans } from '@kit/ui/trans';
import { initiatePayment } from '../../_lib/server/cart-actions';
import AnalysisLocation from './analysis-location';
import CartItems from './cart-items';
import CartServiceItems from './cart-service-items';
import DiscountCode from './discount-code';
import { initiatePayment } from '../../_lib/server/cart-actions';
import { useRouter } from 'next/navigation';
import { AccountBalanceSummary } from '@kit/accounts/services/account-balance.service';
import { EnrichedCartItem } from './types';
const IS_DISCOUNT_SHOWN = true as boolean;
@@ -71,12 +72,13 @@ export default function Cart({
setIsInitiatingSession(true);
try {
const { url, isFullyPaidByBenefits, orderId, unavailableLineItemIds } = await initiatePayment({
accountId,
balanceSummary: balanceSummary!,
cart: cart!,
language,
});
const { url, isFullyPaidByBenefits, orderId, unavailableLineItemIds } =
await initiatePayment({
accountId,
balanceSummary: balanceSummary!,
cart: cart!,
language,
});
if (unavailableLineItemIds) {
setUnavailableLineItemIds(unavailableLineItemIds);
}
@@ -97,7 +99,10 @@ export default function Cart({
const isLocationsShown = synlabAnalyses.length > 0;
const companyBenefitsTotal = balanceSummary?.totalBalance ?? 0;
const montonioTotal = cart && companyBenefitsTotal > 0 ? cart.total - companyBenefitsTotal : cart.total;
const montonioTotal =
cart && companyBenefitsTotal > 0
? cart.total - companyBenefitsTotal
: cart.total;
return (
<div className="small:grid-cols-[1fr_360px] grid grid-cols-1 gap-x-40 lg:px-4">
@@ -158,7 +163,10 @@ export default function Cart({
<div className={`sm:mr-[112px] sm:w-[50px]`}>
<p className="text-right text-sm">
{formatCurrency({
value: (companyBenefitsTotal > cart.total ? cart.total : companyBenefitsTotal),
value:
companyBenefitsTotal > cart.total
? cart.total
: companyBenefitsTotal,
currencyCode: cart.currency_code,
locale: language,
})}

View File

@@ -1,5 +1,6 @@
import { StoreCartLineItem } from "@medusajs/types";
import { Reservation } from "~/lib/types/reservation";
import { StoreCartLineItem } from '@medusajs/types';
import { Reservation } from '~/lib/types/reservation';
export interface MontonioOrderToken {
uuid: string;
@@ -12,7 +13,7 @@ export interface MontonioOrderToken {
| 'CANCELLED'
| 'PENDING'
| 'EXPIRED'
| 'REFUNDED';
| 'REFUNDED'
| 'PAID'
| 'FAILED'
| 'CANCELLED'

View File

@@ -1,5 +1,7 @@
import Link from 'next/link';
import { createI18nServerInstance } from '@/lib/i18n/i18n.server';
import { formatCurrency } from '@/packages/shared/src/utils';
import { ChevronRight, HeartPulse } from 'lucide-react';
import { Button } from '@kit/ui/button';
@@ -11,25 +13,27 @@ import {
CardHeader,
CardTitle,
} from '@kit/ui/card';
import { Trans } from '@kit/ui/trans';
import { formatCurrency } from '@/packages/shared/src/utils';
import { createI18nServerInstance } from '@/lib/i18n/i18n.server';
import { cn } from '@kit/ui/lib/utils';
import { loadCurrentUserAccount } from '../_lib/server/load-user-account';
import { Trans } from '@kit/ui/trans';
import { getAccountBalanceSummary } from '../_lib/server/balance-actions';
import { loadCurrentUserAccount } from '../_lib/server/load-user-account';
export default async function DashboardCards() {
const { language } = await createI18nServerInstance();
const { account } = await loadCurrentUserAccount();
const balanceSummary = account ? await getAccountBalanceSummary(account.id) : null;
const balanceSummary = account
? await getAccountBalanceSummary(account.id)
: null;
return (
<div
className={cn(
'grid grid-cols-1 gap-4',
'md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
)}>
)}
>
<Card
variant="gradient-success"
className="xs:w-1/2 flex w-full flex-col justify-between sm:w-auto"
@@ -79,7 +83,7 @@ export default async function DashboardCards() {
<Trans
i18nKey="dashboard:heroCard.benefits.validUntil"
values={{ date: '31.12.2025' }}
/>
/>
</CardDescription>
</CardContent>
</Card>

View File

@@ -14,7 +14,10 @@ import {
User,
} from 'lucide-react';
import type { AccountWithParams, BmiThresholds } from '@kit/accounts/types/accounts';
import type {
AccountWithParams,
BmiThresholds,
} from '@kit/accounts/types/accounts';
import { pathsConfig } from '@kit/shared/config';
import { Button } from '@kit/ui/button';
import {

View File

@@ -9,8 +9,8 @@ import { useTranslation } from 'react-i18next';
import { Trans } from '@kit/ui/trans';
const PaymentProviderIds = {
COMPANY_BENEFITS: "pp_company-benefits_company-benefits",
MONTONIO: "pp_montonio_montonio",
COMPANY_BENEFITS: 'pp_company-benefits_company-benefits',
MONTONIO: 'pp_montonio_montonio',
};
export default function CartTotals({
@@ -30,10 +30,12 @@ export default function CartTotals({
payment_collections,
} = medusaOrder;
const montonioPayment = payment_collections?.[0]?.payments
?.find(({ provider_id }) => provider_id === PaymentProviderIds.MONTONIO);
const companyBenefitsPayment = payment_collections?.[0]?.payments
?.find(({ provider_id }) => provider_id === PaymentProviderIds.COMPANY_BENEFITS);
const montonioPayment = payment_collections?.[0]?.payments?.find(
({ provider_id }) => provider_id === PaymentProviderIds.MONTONIO,
);
const companyBenefitsPayment = payment_collections?.[0]?.payments?.find(
({ provider_id }) => provider_id === PaymentProviderIds.COMPANY_BENEFITS,
);
return (
<div>
@@ -96,7 +98,6 @@ export default function CartTotals({
</span>
</div>
)}
</div>
<div className="my-4 h-px w-full border-b border-gray-200" />
@@ -126,7 +127,10 @@ export default function CartTotals({
<span className="flex items-center gap-x-1">
<Trans i18nKey="cart:order.benefitsTotal" />
</span>
<span data-testid="cart-subtotal" data-value={companyBenefitsPayment.amount || 0}>
<span
data-testid="cart-subtotal"
data-value={companyBenefitsPayment.amount || 0}
>
-{' '}
{formatCurrency({
value: companyBenefitsPayment.amount ?? 0,
@@ -142,7 +146,10 @@ export default function CartTotals({
<span className="flex items-center gap-x-1">
<Trans i18nKey="cart:order.montonioTotal" />
</span>
<span data-testid="cart-subtotal" data-value={montonioPayment.amount || 0}>
<span
data-testid="cart-subtotal"
data-value={montonioPayment.amount || 0}
>
-{' '}
{formatCurrency({
value: montonioPayment.amount ?? 0,