diff --git a/.env.development b/.env.development index 2cc0b56..ac3067a 100644 --- a/.env.development +++ b/.env.development @@ -34,6 +34,7 @@ MEDIPOST_PASSWORD=SRB48HZMV MEDIPOST_RECIPIENT=trvurgtst MEDIPOST_MESSAGE_SENDER=trvurgtst MEDIPOST_ENABLE_DISPATCH_ON_MONTONIO_CALLBACK=true +MEDIPOST_ENABLE_DELETE_RESPONSE_PRIVATE_MESSAGE_ON_READ=false #MEDIPOST_URL=https://medipost2.medisoft.ee:8443/Medipost/MedipostServlet #MEDIPOST_USER=medreport @@ -43,6 +44,7 @@ MEDIPOST_ENABLE_DISPATCH_ON_MONTONIO_CALLBACK=true #MEDIPOST_ENABLE_DISPATCH_ON_MONTONIO_CALLBACK=false # MEDUSA +COMPANY_BENEFITS_PAYMENT_SECRET_KEY=NzcwMzE2NmEtOThiMS0xMWYwLWI4NjYtMDMwZDQzMjFhMjExCg== MEDUSA_BACKEND_URL=http://localhost:9000 MEDUSA_BACKEND_PUBLIC_URL=http://localhost:9000 diff --git a/app/api/job/handler/sync-connected-online.ts b/app/api/job/handler/sync-connected-online.ts index 07b7435..80e89c9 100644 --- a/app/api/job/handler/sync-connected-online.ts +++ b/app/api/job/handler/sync-connected-online.ts @@ -38,7 +38,9 @@ function getSpokenLanguages(spokenLanguages?: string) { } export default async function syncConnectedOnline() { - const isProd = process.env.NODE_ENV === 'production'; + const isProd = !['test', 'localhost'].some((pathString) => + process.env.NEXT_PUBLIC_SITE_URL?.includes(pathString), + ); const baseUrl = process.env.CONNECTED_ONLINE_URL; @@ -84,34 +86,20 @@ export default async function syncConnectedOnline() { let serviceProviders; let jobTitleTranslations; // Filter out "Dentas Demo OÜ" in prod or only sync "Dentas Demo OÜ" in any other environment - const isDemoClinic = (clinicId: number) => clinicId === 2; - if (isProd) { - clinics = responseData.Data.T_Lic.filter(({ ID }) => !isDemoClinic(ID)); - services = responseData.Data.T_Service.filter( - ({ ClinicID }) => !isDemoClinic(ClinicID), - ); - serviceProviders = responseData.Data.T_Doctor.filter( - ({ ClinicID }) => !isDemoClinic(ClinicID), - ); - jobTitleTranslations = createTranslationMap( - responseData.Data.P_JobTitleTranslations.filter( - ({ ClinicID }) => !isDemoClinic(ClinicID), - ), - ); - } else { - clinics = responseData.Data.T_Lic.filter(({ ID }) => isDemoClinic(ID)); - services = responseData.Data.T_Service.filter(({ ClinicID }) => + const isDemoClinic = (clinicId: number) => + isProd ? clinicId !== 2 : clinicId === 2; + clinics = responseData.Data.T_Lic.filter(({ ID }) => isDemoClinic(ID)); + services = responseData.Data.T_Service.filter(({ ClinicID }) => + isDemoClinic(ClinicID), + ); + serviceProviders = responseData.Data.T_Doctor.filter(({ ClinicID }) => + isDemoClinic(ClinicID), + ); + jobTitleTranslations = createTranslationMap( + responseData.Data.P_JobTitleTranslations.filter(({ ClinicID }) => isDemoClinic(ClinicID), - ); - serviceProviders = responseData.Data.T_Doctor.filter(({ ClinicID }) => - isDemoClinic(ClinicID), - ); - jobTitleTranslations = createTranslationMap( - responseData.Data.P_JobTitleTranslations.filter(({ ClinicID }) => - isDemoClinic(ClinicID), - ), - ); - } + ), + ); const mappedClinics = clinics.map((clinic) => { return { diff --git a/app/auth/update-account/_lib/server/update-account.ts b/app/auth/update-account/_lib/server/update-account.ts index 8bca7bb..622787d 100644 --- a/app/auth/update-account/_lib/server/update-account.ts +++ b/app/auth/update-account/_lib/server/update-account.ts @@ -16,7 +16,6 @@ export const onUpdateAccount = enhanceAction( try { await api.updateAccount(params); - console.log('SUCCESS', pathsConfig.auth.updateAccountSuccess); } catch (err: unknown) { if (err instanceof Error) { console.warn('On update account error: ' + err.message); diff --git a/app/home/(user)/(dashboard)/cart/montonio-callback/actions.ts b/app/home/(user)/(dashboard)/cart/montonio-callback/actions.ts index b1ccb05..a23660c 100644 --- a/app/home/(user)/(dashboard)/cart/montonio-callback/actions.ts +++ b/app/home/(user)/(dashboard)/cart/montonio-callback/actions.ts @@ -2,107 +2,13 @@ import { MontonioOrderToken } from '@/app/home/(user)/_components/cart/types'; import { loadCurrentUserAccount } from '@/app/home/(user)/_lib/server/load-user-account'; -import { placeOrder, retrieveCart } from '@lib/data/cart'; -import { listProductTypes } from '@lib/data/products'; -import type { StoreOrder } from '@medusajs/types'; +import { retrieveCart } from '@lib/data/cart'; import jwt from 'jsonwebtoken'; -import { z } from 'zod'; -import { AccountWithParams } from '@kit/accounts/types/accounts'; -import { createNotificationsApi } from '@kit/notifications/api'; -import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client'; +import { handlePlaceOrder } from '../../../_lib/server/cart-actions'; -import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; -import { bookAppointment } from '~/lib/services/connected-online.service'; -import { sendOrderToMedipost } from '~/lib/services/medipost/medipostPrivateMessage.service'; -import { getOrderedAnalysisIds } from '~/lib/services/medusaOrder.service'; -import { - createAnalysisOrder, - getAnalysisOrder, -} from '~/lib/services/order.service'; -import { getOrderedTtoServices } from '~/lib/services/reservation.service'; -import { FailureReason } from '~/lib/types/connected-online'; - -const ANALYSIS_PACKAGES_TYPE_HANDLE = 'analysis-packages'; -const ANALYSIS_TYPE_HANDLE = 'synlab-analysis'; const MONTONIO_PAID_STATUS = 'PAID'; -const env = () => - z - .object({ - emailSender: z - .string({ - error: 'EMAIL_SENDER is required', - }) - .min(1), - siteUrl: z - .string({ - error: 'NEXT_PUBLIC_SITE_URL is required', - }) - .min(1), - isEnabledDispatchOnMontonioCallback: z.boolean({ - error: 'MEDIPOST_ENABLE_DISPATCH_ON_MONTONIO_CALLBACK is required', - }), - }) - .parse({ - emailSender: process.env.EMAIL_SENDER, - siteUrl: process.env.NEXT_PUBLIC_SITE_URL!, - isEnabledDispatchOnMontonioCallback: - process.env.MEDIPOST_ENABLE_DISPATCH_ON_MONTONIO_CALLBACK === 'true', - }); - -const sendEmail = async ({ - account, - email, - analysisPackageName, - partnerLocationName, - language, -}: { - account: Pick; - email: string; - analysisPackageName: string; - partnerLocationName: string; - language: string; -}) => { - const client = getSupabaseServerAdminClient(); - try { - const { renderSynlabAnalysisPackageEmail } = await import( - '@kit/email-templates' - ); - const { getMailer } = await import('@kit/mailers'); - - const mailer = await getMailer(); - - const { html, subject } = await renderSynlabAnalysisPackageEmail({ - analysisPackageName, - personName: account.name, - partnerLocationName, - language, - }); - - await mailer - .sendEmail({ - from: env().emailSender, - to: email, - subject, - html, - }) - .catch((error) => { - throw new Error(`Failed to send email, message=${error}`); - }); - await createNotificationsApi(client).createNotification({ - account_id: account.id, - body: html, - }); - await createNotificationsApi(client).createNotification({ - account_id: account.id, - body: html, - }); - } catch (error) { - throw new Error(`Failed to send email, message=${error}`); - } -}; - async function decodeOrderToken(orderToken: string) { const secretKey = process.env.MONTONIO_SECRET_KEY as string; @@ -129,71 +35,6 @@ async function getCartByOrderToken(decoded: MontonioOrderToken) { return cart; } -async function getOrderResultParameters(medusaOrder: StoreOrder) { - const { productTypes } = await listProductTypes(); - const analysisPackagesType = productTypes.find( - ({ metadata }) => metadata?.handle === ANALYSIS_PACKAGES_TYPE_HANDLE, - ); - const analysisType = productTypes.find( - ({ metadata }) => metadata?.handle === ANALYSIS_TYPE_HANDLE, - ); - - const analysisPackageOrderItem = medusaOrder.items?.find( - ({ product_type_id }) => product_type_id === analysisPackagesType?.id, - ); - const analysisItems = medusaOrder.items?.filter( - ({ product_type_id }) => product_type_id === analysisType?.id, - ); - - return { - medusaOrderId: medusaOrder.id, - email: medusaOrder.email, - analysisPackageOrder: analysisPackageOrderItem - ? { - partnerLocationName: - (analysisPackageOrderItem?.metadata - ?.partner_location_name as string) ?? '', - analysisPackageName: analysisPackageOrderItem?.title ?? '', - } - : null, - analysisItemsOrder: - Array.isArray(analysisItems) && analysisItems.length > 0 - ? analysisItems.map(({ product }) => ({ - analysisName: product?.title ?? '', - analysisId: (product?.metadata?.analysisIdOriginal as string) ?? '', - })) - : null, - }; -} - -async function sendAnalysisPackageOrderEmail({ - account, - email, - analysisPackageOrder, -}: { - account: AccountWithParams; - email: string; - analysisPackageOrder: { - partnerLocationName: string; - analysisPackageName: string; - }; -}) { - const { language } = await createI18nServerInstance(); - const { analysisPackageName, partnerLocationName } = analysisPackageOrder; - try { - await sendEmail({ - account: { id: account.id, name: account.name }, - email, - analysisPackageName, - partnerLocationName, - language, - }); - console.info(`Successfully sent analysis package order email to ${email}`); - } catch (error) { - console.error('Failed to send email', error); - } -} - export async function processMontonioCallback(orderToken: string) { const { account } = await loadCurrentUserAccount(); if (!account) { @@ -203,99 +44,8 @@ export async function processMontonioCallback(orderToken: string) { try { const decoded = await decodeOrderToken(orderToken); const cart = await getCartByOrderToken(decoded); - - const medusaOrder = await placeOrder(cart.id, { - revalidateCacheTags: false, - }); - const orderedAnalysisElements = await getOrderedAnalysisIds({ - medusaOrder, - }); - - const orderContainsSynlabItems = !!orderedAnalysisElements?.length; - - try { - const existingAnalysisOrder = await getAnalysisOrder({ - medusaOrderId: medusaOrder.id, - }); - console.info( - `Analysis order already exists for medusaOrderId=${medusaOrder.id}, orderId=${existingAnalysisOrder.id}`, - ); - return { success: true, orderId: existingAnalysisOrder.id }; - } catch { - // ignored - } - - let orderId; - 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({ - account, - email, - analysisPackageOrder, - }); - } else { - console.info(`Order has no analysis package, skipping email.`); - } - - if (analysisItemsOrder) { - // @TODO send email for separate analyses - console.warn( - `Order has analysis items, but no email template exists yet`, - ); - } else { - console.info(`Order has no analysis items, skipping email.`); - } - } else { - console.error('Missing email to send order result email', orderResult); - } - - if (env().isEnabledDispatchOnMontonioCallback && orderContainsSynlabItems) { - 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 }; + const result = await handlePlaceOrder({ cart }); + return result; } catch (error) { console.error('Failed to place order', error); throw new Error(`Failed to place order, message=${error}`); diff --git a/app/home/(user)/(dashboard)/cart/page.tsx b/app/home/(user)/(dashboard)/cart/page.tsx index 4a2af1f..389ee2d 100644 --- a/app/home/(user)/(dashboard)/cart/page.tsx +++ b/app/home/(user)/(dashboard)/cart/page.tsx @@ -1,10 +1,9 @@ -import { notFound } from 'next/navigation'; - import { createI18nServerInstance } from '@/lib/i18n/i18n.server'; import { PageBody, PageHeader } from '@/packages/ui/src/makerkit/page'; import { retrieveCart } from '@lib/data/cart'; import { listProductTypes } from '@lib/data/products'; +import { AccountBalanceService } from '@kit/accounts/services/account-balance.service'; import { Trans } from '@kit/ui/trans'; import { withI18n } from '~/lib/i18n/with-i18n'; @@ -14,6 +13,7 @@ import { findProductTypeIdByHandle } from '~/lib/utils'; import Cart from '../../_components/cart'; import CartTimer from '../../_components/cart/cart-timer'; import { EnrichedCartItem } from '../../_components/cart/types'; +import { loadCurrentUserAccount } from '../../_lib/server/load-user-account'; export async function generateMetadata() { const { t } = await createI18nServerInstance(); @@ -24,12 +24,19 @@ export async function generateMetadata() { } async function CartPage() { - const cart = await retrieveCart().catch((error) => { - console.error('Failed to retrieve cart', error); - return notFound(); - }); + const [cart, { productTypes }, { account }] = await Promise.all([ + retrieveCart(), + listProductTypes(), + loadCurrentUserAccount(), + ]); - const { productTypes } = await listProductTypes(); + if (!account) { + return null; + } + + const balanceSummary = await new AccountBalanceService().getBalanceSummary( + account.id, + ); const synlabAnalysisTypeId = findProductTypeIdByHandle( productTypes, @@ -70,9 +77,11 @@ async function CartPage() { {isTimerShown && } ); diff --git a/app/home/(user)/(dashboard)/order/[orderId]/confirmed/order-confirmed-loading-wrapper.tsx b/app/home/(user)/(dashboard)/order/[orderId]/confirmed/order-confirmed-loading-wrapper.tsx new file mode 100644 index 0000000..7e99300 --- /dev/null +++ b/app/home/(user)/(dashboard)/order/[orderId]/confirmed/order-confirmed-loading-wrapper.tsx @@ -0,0 +1,83 @@ +'use client'; + +import { useEffect, useRef, useState } from 'react'; + +import CartTotals from '@/app/home/(user)/_components/order/cart-totals'; +import OrderDetails from '@/app/home/(user)/_components/order/order-details'; +import OrderItems from '@/app/home/(user)/_components/order/order-items'; +import { retrieveOrder } from '@lib/data/orders'; +import { StoreOrder } from '@medusajs/types'; +import Divider from '@modules/common/components/divider'; + +import { GlobalLoader } from '@kit/ui/makerkit/global-loader'; +import { PageBody, PageHeader } from '@kit/ui/page'; +import { Trans } from '@kit/ui/trans'; + +import { AnalysisOrder } from '~/lib/types/analysis-order'; + +function OrderConfirmedLoadingWrapper({ + medusaOrder: initialMedusaOrder, + order, +}: { + medusaOrder: StoreOrder; + order: AnalysisOrder; +}) { + const [medusaOrder, setMedusaOrder] = + useState(initialMedusaOrder); + const fetchingRef = useRef(false); + + const paymentStatus = medusaOrder.payment_status; + const medusaOrderId = order.medusa_order_id; + + useEffect(() => { + if (paymentStatus === 'captured') { + return; + } + + const interval = setInterval(async () => { + if (fetchingRef.current) { + return; + } + + fetchingRef.current = true; + const medusaOrder = await retrieveOrder(medusaOrderId, false); + fetchingRef.current = false; + + setMedusaOrder(medusaOrder); + }, 2_000); + + return () => clearInterval(interval); + }, [paymentStatus, medusaOrderId]); + + const isPaid = paymentStatus === 'captured'; + + if (!isPaid) { + return ( + +
+
+ +
+

+ +

+
+
+ ); + } + + return ( + + } /> + +
+ + + + +
+
+ ); +} + +export default OrderConfirmedLoadingWrapper; diff --git a/app/home/(user)/(dashboard)/order/[orderId]/confirmed/page.tsx b/app/home/(user)/(dashboard)/order/[orderId]/confirmed/page.tsx index eeebd7b..4477532 100644 --- a/app/home/(user)/(dashboard)/order/[orderId]/confirmed/page.tsx +++ b/app/home/(user)/(dashboard)/order/[orderId]/confirmed/page.tsx @@ -1,19 +1,15 @@ import { redirect } from 'next/navigation'; -import CartTotals from '@/app/home/(user)/_components/order/cart-totals'; -import OrderDetails from '@/app/home/(user)/_components/order/order-details'; -import OrderItems from '@/app/home/(user)/_components/order/order-items'; import { createI18nServerInstance } from '@/lib/i18n/i18n.server'; import { retrieveOrder } from '@lib/data/orders'; -import Divider from '@modules/common/components/divider'; import { pathsConfig } from '@kit/shared/config'; -import { PageBody, PageHeader } from '@kit/ui/page'; -import { Trans } from '@kit/ui/trans'; import { withI18n } from '~/lib/i18n/with-i18n'; import { getAnalysisOrder } from '~/lib/services/order.service'; +import OrderConfirmedLoadingWrapper from './order-confirmed-loading-wrapper'; + export async function generateMetadata() { const { t } = await createI18nServerInstance(); @@ -42,16 +38,7 @@ async function OrderConfirmedPage(props: { } return ( - - } /> - -
- - - - -
-
+ ); } diff --git a/app/home/(user)/(dashboard)/order/page.tsx b/app/home/(user)/(dashboard)/order/page.tsx index 284988e..a0982f5 100644 --- a/app/home/(user)/(dashboard)/order/page.tsx +++ b/app/home/(user)/(dashboard)/order/page.tsx @@ -18,6 +18,8 @@ import { listOrders } from '~/medusa/lib/data/orders'; import { HomeLayoutPageHeader } from '../../_components/home-page-header'; import OrderBlock from '../../_components/orders/order-block'; +const ORDERS_LIMIT = 50; + export async function generateMetadata() { const { t } = await createI18nServerInstance(); @@ -27,10 +29,13 @@ export async function generateMetadata() { } async function OrdersPage() { - const medusaOrders = await listOrders(); - const analysisOrders = await getAnalysisOrders(); - const ttoOrders = await getTtoOrders(); - const { productTypes } = await listProductTypes(); + const [medusaOrders, analysisOrders, ttoOrders, { productTypes }] = + await Promise.all([ + listOrders(ORDERS_LIMIT), + getAnalysisOrders(), + getTtoOrders(), + listProductTypes(), + ]); if (!medusaOrders || !productTypes || !ttoOrders) { redirect(pathsConfig.auth.signIn); diff --git a/app/home/(user)/(dashboard)/page.tsx b/app/home/(user)/(dashboard)/page.tsx index 22a671c..cdb7bcf 100644 --- a/app/home/(user)/(dashboard)/page.tsx +++ b/app/home/(user)/(dashboard)/page.tsx @@ -16,6 +16,7 @@ import Dashboard from '../_components/dashboard'; import DashboardCards from '../_components/dashboard-cards'; import Recommendations from '../_components/recommendations'; import RecommendationsSkeleton from '../_components/recommendations-skeleton'; +import { isValidOpenAiEnv } from '../_lib/server/is-valid-open-ai-env'; import { loadCurrentUserAccount } from '../_lib/server/load-user-account'; export const generateMetadata = async () => { @@ -52,17 +53,16 @@ async function UserHomePage() { /> - {process.env.OPENAI_API_KEY && - process.env.PROMPT_ID_ANALYSIS_RECOMMENDATIONS && ( - <> -

- -

- }> - - - - )} + {(await isValidOpenAiEnv()) && ( + <> +

+ +

+ }> + + + + )}
); diff --git a/app/home/(user)/_components/booking/booking-container.tsx b/app/home/(user)/_components/booking/booking-container.tsx index 9d3b6e8..67a9400 100644 --- a/app/home/(user)/_components/booking/booking-container.tsx +++ b/app/home/(user)/_components/booking/booking-container.tsx @@ -32,7 +32,16 @@ const BookingContainer = ({
- + { + if (product.metadata?.serviceIds) { + return Array.isArray( + JSON.parse(product.metadata.serviceIds as string), + ); + } + return false; + })} + />
diff --git a/app/home/(user)/_components/booking/booking-pagination.tsx b/app/home/(user)/_components/booking/booking-pagination.tsx new file mode 100644 index 0000000..f0c4631 --- /dev/null +++ b/app/home/(user)/_components/booking/booking-pagination.tsx @@ -0,0 +1,113 @@ +import React from 'react'; + +import { useTranslation } from 'react-i18next'; + +import { Trans } from '@kit/ui/makerkit/trans'; +import { cn } from '@kit/ui/shadcn'; +import { Button } from '@kit/ui/shadcn/button'; + +const BookingPagination = ({ + totalPages, + setCurrentPage, + currentPage, +}: { + totalPages: number; + setCurrentPage: (page: number) => void; + currentPage: number; +}) => { + const { t } = useTranslation(); + + const generatePageNumbers = () => { + const pages = []; + const maxVisiblePages = 5; + + if (totalPages <= maxVisiblePages) { + for (let i = 1; i <= totalPages; i++) { + pages.push(i); + } + } else { + if (currentPage <= 3) { + for (let i = 1; i <= 4; i++) { + pages.push(i); + } + pages.push('...'); + pages.push(totalPages); + } else if (currentPage >= totalPages - 2) { + pages.push(1); + pages.push('...'); + for (let i = totalPages - 3; i <= totalPages; i++) { + pages.push(i); + } + } else { + pages.push(1); + pages.push('...'); + for (let i = currentPage - 1; i <= currentPage + 1; i++) { + pages.push(i); + } + pages.push('...'); + pages.push(totalPages); + } + } + + return pages; + }; + + if (totalPages === 0) { + return ( +
+

{t('booking:noResults')}

+
+ ); + } + + return ( + totalPages > 1 && ( +
+
+ {t('common:pageOfPages', { + page: currentPage, + total: totalPages, + })} +
+ +
+ + + {generatePageNumbers().map((page, index) => ( + + ))} + + +
+
+ ) + ); +}; + +export default BookingPagination; diff --git a/app/home/(user)/_components/booking/booking.provider.tsx b/app/home/(user)/_components/booking/booking.provider.tsx index b0c21e1..3ae2f1c 100644 --- a/app/home/(user)/_components/booking/booking.provider.tsx +++ b/app/home/(user)/_components/booking/booking.provider.tsx @@ -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, diff --git a/app/home/(user)/_components/booking/time-slots.tsx b/app/home/(user)/_components/booking/time-slots.tsx index 4d5fc14..58190b1 100644 --- a/app/home/(user)/_components/booking/time-slots.tsx +++ b/app/home/(user)/_components/booking/time-slots.tsx @@ -11,6 +11,7 @@ import { pathsConfig } from '@kit/shared/config'; import { formatDateAndTime } from '@kit/shared/utils'; import { Button } from '@kit/ui/shadcn/button'; import { Card } from '@kit/ui/shadcn/card'; +import { Skeleton } from '@kit/ui/shadcn/skeleton'; import { toast } from '@kit/ui/sonner'; import { Trans } from '@kit/ui/trans'; import { cn } from '@kit/ui/utils'; @@ -19,6 +20,7 @@ import { updateReservationTime } from '~/lib/services/reservation.service'; import { createInitialReservationAction } from '../../_lib/server/actions'; import { EnrichedCartItem } from '../cart/types'; +import BookingPagination from './booking-pagination'; import { ServiceProvider, TimeSlot } from './booking.context'; import { useBooking } from './booking.provider'; @@ -68,57 +70,16 @@ const TimeSlots = ({ }) ?? [], 'StartTime', 'asc', - ), + ).filter(({ StartTime }) => isSameDay(StartTime, selectedDate)), [booking.timeSlots, selectedDate], ); - const totalPages = Math.ceil(filteredBookings.length / PAGE_SIZE); - const paginatedBookings = useMemo(() => { const startIndex = (currentPage - 1) * PAGE_SIZE; const endIndex = startIndex + PAGE_SIZE; return filteredBookings.slice(startIndex, endIndex); }, [filteredBookings, currentPage, PAGE_SIZE]); - const handlePageChange = (page: number) => { - setCurrentPage(page); - }; - - const generatePageNumbers = () => { - const pages = []; - const maxVisiblePages = 5; - - if (totalPages <= maxVisiblePages) { - for (let i = 1; i <= totalPages; i++) { - pages.push(i); - } - } else { - if (currentPage <= 3) { - for (let i = 1; i <= 4; i++) { - pages.push(i); - } - pages.push('...'); - pages.push(totalPages); - } else if (currentPage >= totalPages - 2) { - pages.push(1); - pages.push('...'); - for (let i = totalPages - 3; i <= totalPages; i++) { - pages.push(i); - } - } else { - pages.push(1); - pages.push('...'); - for (let i = currentPage - 1; i <= currentPage + 1; i++) { - pages.push(i); - } - pages.push('...'); - pages.push(totalPages); - } - } - - return pages; - }; - if (!booking?.timeSlots?.length) { return null; } @@ -143,12 +104,17 @@ const TimeSlots = ({ timeSlot.StartTime, booking.selectedLocationId ? booking.selectedLocationId : null, comments, - ).then(() => { - if (onComplete) { - onComplete(); - } - router.push(pathsConfig.app.cart); - }); + ) + .then(() => { + if (onComplete) { + onComplete(); + } + router.push(pathsConfig.app.cart); + }) + .catch((error) => { + console.error('Booking error: ', error); + throw error; + }); toast.promise(() => bookTimePromise, { success: , @@ -203,10 +169,13 @@ const TimeSlots = ({ }; return ( -
+
{paginatedBookings.map((timeSlot, index) => { - const isEHIF = timeSlot.HKServiceID > 0; + const isHaigeKassa = timeSlot.HKServiceID > 0; const serviceProviderTitle = getServiceProviderTitle( currentLocale, timeSlot.serviceProvider, @@ -214,6 +183,7 @@ const TimeSlots = ({ const price = booking.selectedService?.variants?.[0]?.calculated_price ?.calculated_amount ?? cartItem?.unit_price; + return (
@@ -232,12 +202,14 @@ const TimeSlots = ({
{serviceProviderTitle && ( {serviceProviderTitle} )} - {isEHIF && {t('booking:ehifBooking')}} + {isHaigeKassa && {t('booking:ehifBooking')}}
{timeSlot.location?.address}
@@ -256,63 +228,14 @@ const TimeSlots = ({ ); })} - - {!paginatedBookings.length && ( -
-

{t('booking:noResults')}

-
- )}
- {totalPages > 1 && ( -
-
- {t('common:pageOfPages', { - page: currentPage, - total: totalPages, - })} -
- -
- - - {generatePageNumbers().map((page, index) => ( - - ))} - - -
-
- )} - + + ); }; diff --git a/app/home/(user)/_components/cart/index.tsx b/app/home/(user)/_components/cart/index.tsx index 81fbdd4..6ac2c76 100644 --- a/app/home/(user)/_components/cart/index.tsx +++ b/app/home/(user)/_components/cart/index.tsx @@ -2,17 +2,19 @@ import { useState } from 'react'; -import { handleNavigateToPayment } from '@/lib/services/medusaCart.service'; +import { useRouter } from 'next/navigation'; + import { formatCurrency } from '@/packages/shared/src/utils'; -import { initiatePaymentSession } from '@lib/data/cart'; 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'; @@ -22,25 +24,31 @@ import { EnrichedCartItem } from './types'; const IS_DISCOUNT_SHOWN = true as boolean; export default function Cart({ + accountId, cart, synlabAnalyses, ttoServiceItems, + balanceSummary, }: { + accountId: string; cart: StoreCart | null; synlabAnalyses: StoreCartLineItem[]; ttoServiceItems: EnrichedCartItem[]; + balanceSummary: AccountBalanceSummary | null; }) { const { i18n: { language }, } = useTranslation(); const [isInitiatingSession, setIsInitiatingSession] = useState(false); + const router = useRouter(); const [unavailableLineItemIds, setUnavailableLineItemIds] = useState(); const items = cart?.items ?? []; + const hasCartItems = cart && Array.isArray(items) && items.length > 0; - if (!cart || items.length === 0) { + if (!hasCartItems) { return (
@@ -60,32 +68,42 @@ export default function Cart({ ); } - async function initiatePayment() { + async function initiateSession() { setIsInitiatingSession(true); - const response = await initiatePaymentSession(cart!, { - provider_id: 'pp_montonio_montonio', - }); - if (response.payment_collection) { - const { payment_sessions } = response.payment_collection; - const paymentSessionId = payment_sessions![0]!.id; - const result = await handleNavigateToPayment({ - language, - paymentSessionId, - }); - if (result.url) { - window.location.href = result.url; + + try { + const { url, isFullyPaidByBenefits, orderId, unavailableLineItemIds } = + await initiatePayment({ + accountId, + balanceSummary: balanceSummary!, + cart: cart!, + language, + }); + if (unavailableLineItemIds) { + setUnavailableLineItemIds(unavailableLineItemIds); } - if (result.unavailableLineItemIds) { - setUnavailableLineItemIds(result.unavailableLineItemIds); + if (url) { + window.location.href = url; + } else if (isFullyPaidByBenefits) { + if (typeof orderId !== 'number') { + throw new Error('Order ID is missing'); + } + router.push(`/home/order/${orderId}/confirmed`); } - } else { + } catch (error) { + console.error('Failed to initiate payment', error); setIsInitiatingSession(false); } } - const hasCartItems = Array.isArray(cart.items) && cart.items.length > 0; const isLocationsShown = synlabAnalyses.length > 0; + const companyBenefitsTotal = balanceSummary?.totalBalance ?? 0; + const montonioTotal = + cart && companyBenefitsTotal > 0 + ? cart.total - companyBenefitsTotal + : cart.total; + return (
@@ -119,7 +137,7 @@ export default function Cart({

-
+

@@ -135,6 +153,27 @@ export default function Cart({

+ {companyBenefitsTotal > 0 && ( +
+
+

+ +

+
+
+

+ {formatCurrency({ + value: + companyBenefitsTotal > cart.total + ? cart.total + : companyBenefitsTotal, + currencyCode: cart.currency_code, + locale: language, + })} +

+
+
+ )}

@@ -144,7 +183,7 @@ export default function Cart({

{formatCurrency({ - value: cart.total, + value: montonioTotal < 0 ? 0 : montonioTotal, currencyCode: cart.currency_code, locale: language, })} @@ -180,10 +219,6 @@ export default function Cart({ cart={{ ...cart }} synlabAnalyses={synlabAnalyses} /> - )} @@ -192,7 +227,7 @@ export default function Cart({

-
- -
-

- -

-

- -

- - - -
- -
- + - 1800 € + + {accountBenefitStatistics.companyAccountsCount} + + + + + + + + {formatCurrency({ + value: accountBenefitStatistics.orders.totalSum, + locale: language, + currencyCode: 'EUR', + })} + + + + + + + + + {formatCurrency({ + value: expensesOverview.currentMonthUsageTotal, + locale: language, + currencyCode: 'EUR', + })} + + + - 200 € + + {formatCurrency({ + value: accountBenefitStatistics.orders.analysesSum, + locale: language, + currencyCode: 'EUR', + })} + - - - - - - - 200 € - - - - - - - - - 200 € - - - - - - - - - 200 € - - - + - 200 € + + {formatCurrency({ + value: accountBenefitStatistics.orders.analysisPackagesSum, + locale: language, + currencyCode: 'EUR', + })} + diff --git a/app/home/[account]/_components/team-account-health-details.tsx b/app/home/[account]/_components/team-account-health-details.tsx index 666a029..6ee3a0d 100644 --- a/app/home/[account]/_components/team-account-health-details.tsx +++ b/app/home/[account]/_components/team-account-health-details.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Database } from '@/packages/supabase/src/database.types'; +import type { BmiThresholds } from '@kit/accounts/types/accounts'; import { Card } from '@kit/ui/card'; import { Trans } from '@kit/ui/trans'; import { cn } from '@kit/ui/utils'; @@ -15,10 +16,7 @@ const TeamAccountHealthDetails = ({ members, }: { memberParams: TeamAccountStatisticsProps['memberParams']; - bmiThresholds: Omit< - Database['medreport']['Tables']['bmi_thresholds']['Row'], - 'id' - >[]; + bmiThresholds: Omit[]; members: Database['medreport']['Functions']['get_account_members']['Returns']; }) => { const accountHealthDetailsFields = getAccountHealthDetailsFields( diff --git a/app/home/[account]/_components/team-account-statistics.tsx b/app/home/[account]/_components/team-account-statistics.tsx index fe8af56..d921b5a 100644 --- a/app/home/[account]/_components/team-account-statistics.tsx +++ b/app/home/[account]/_components/team-account-statistics.tsx @@ -4,6 +4,11 @@ import { useState } from 'react'; import { redirect } from 'next/navigation'; +import type { + Account, + AccountParams, + BmiThresholds, +} from '@/packages/features/accounts/src/types/accounts'; import { Database } from '@/packages/supabase/src/database.types'; import { format } from 'date-fns'; import { enGB, et } from 'date-fns/locale'; @@ -14,28 +19,20 @@ import { createPath, pathsConfig } from '@kit/shared/config'; import { Card } from '@kit/ui/card'; import { Trans } from '@kit/ui/makerkit/trans'; import { Button } from '@kit/ui/shadcn/button'; -import { Calendar, DateRange } from '@kit/ui/shadcn/calendar'; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from '@kit/ui/shadcn/popover'; +import { DateRange } from '@kit/ui/shadcn/calendar'; +import { TeamAccountBenefitExpensesOverview } from '../_lib/server/load-team-account-benefit-expenses-overview'; +import { AccountBenefitStatistics } from '../_lib/server/load-team-account-benefit-statistics'; import TeamAccountBenefitStatistics from './team-account-benefit-statistics'; import TeamAccountHealthDetails from './team-account-health-details'; export interface TeamAccountStatisticsProps { - teamAccount: Database['medreport']['Tables']['accounts']['Row']; - memberParams: Pick< - Database['medreport']['Tables']['account_params']['Row'], - 'weight' | 'height' - >[]; - bmiThresholds: Omit< - Database['medreport']['Tables']['bmi_thresholds']['Row'], - 'id' - >[]; + teamAccount: Account; + memberParams: Pick[]; + bmiThresholds: Omit[]; members: Database['medreport']['Functions']['get_account_members']['Returns']; - companyParams: Database['medreport']['Tables']['company_params']['Row']; + accountBenefitStatistics: AccountBenefitStatistics; + expensesOverview: TeamAccountBenefitExpensesOverview; } export default function TeamAccountStatistics({ @@ -43,11 +40,13 @@ export default function TeamAccountStatistics({ memberParams, bmiThresholds, members, - companyParams, + accountBenefitStatistics, + expensesOverview, }: TeamAccountStatisticsProps) { + const currentDate = new Date(); const [date, setDate] = useState({ - from: new Date(), - to: new Date(), + from: new Date(currentDate.getFullYear(), currentDate.getMonth(), 1), + to: new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0), }); const { i18n: { language }, @@ -58,7 +57,7 @@ export default function TeamAccountStatistics({ return ( <> -
+

- - - - - - - - +
@@ -146,10 +132,7 @@ export default function TeamAccountStatistics({ className="border-warning/40 hover:bg-warning/20 relative flex h-full cursor-pointer flex-col justify-center px-6 py-4 transition-colors" onClick={() => redirect( - createPath( - pathsConfig.app.accountBilling, - teamAccount.slug || '', - ), + createPath(pathsConfig.app.accountBilling, teamAccount.slug!), ) } > diff --git a/app/home/[account]/_lib/server/load-team-account-benefit-expenses-overview.ts b/app/home/[account]/_lib/server/load-team-account-benefit-expenses-overview.ts new file mode 100644 index 0000000..061d8b0 --- /dev/null +++ b/app/home/[account]/_lib/server/load-team-account-benefit-expenses-overview.ts @@ -0,0 +1,81 @@ +import { getSupabaseServerClient } from '@/packages/supabase/src/clients/server-client'; + +import { loadCompanyPersonalAccountsBalanceEntries } from './load-team-account-benefit-statistics'; + +export interface TeamAccountBenefitExpensesOverview { + benefitAmount: number | null; + benefitOccurrence: 'yearly' | 'monthly' | 'quarterly' | null; + currentMonthUsageTotal: number; + managementFee: number; + managementFeeTotal: number; + total: number; +} + +const MANAGEMENT_FEE = 5.5; + +const MONTHS = 12; +const QUARTERS = 4; + +export async function loadTeamAccountBenefitExpensesOverview({ + companyId, + employeeCount, +}: { + companyId: string; + employeeCount: number; +}): Promise { + const supabase = getSupabaseServerClient(); + const { data, error } = await supabase + .schema('medreport') + .from('benefit_distribution_schedule') + .select('*') + .eq('company_id', companyId) + .eq('is_active', true) + .single(); + + let benefitAmount: TeamAccountBenefitExpensesOverview['benefitAmount'] = null; + let benefitOccurrence: TeamAccountBenefitExpensesOverview['benefitOccurrence'] = + null; + if (error) { + console.warn('Failed to load team account benefit expenses overview'); + } else { + benefitAmount = + data.benefit_amount as TeamAccountBenefitExpensesOverview['benefitAmount']; + benefitOccurrence = + data.benefit_occurrence as TeamAccountBenefitExpensesOverview['benefitOccurrence']; + } + + const { purchaseEntriesTotal } = + await loadCompanyPersonalAccountsBalanceEntries({ accountId: companyId }); + + return { + benefitAmount, + benefitOccurrence, + currentMonthUsageTotal: purchaseEntriesTotal, + managementFee: MANAGEMENT_FEE, + managementFeeTotal: MANAGEMENT_FEE * employeeCount, + total: (() => { + if (typeof benefitAmount !== 'number') { + return 0; + } + + const currentDate = new Date(); + const createdAt = new Date(data.created_at); + const isCreatedThisYear = + createdAt.getFullYear() === currentDate.getFullYear(); + if (benefitOccurrence === 'yearly') { + return benefitAmount * employeeCount; + } else if (benefitOccurrence === 'monthly') { + const monthsLeft = isCreatedThisYear + ? MONTHS - createdAt.getMonth() + : MONTHS; + return benefitAmount * employeeCount * monthsLeft; + } else if (benefitOccurrence === 'quarterly') { + const quartersLeft = isCreatedThisYear + ? QUARTERS - Math.ceil(createdAt.getMonth() / 3) + : QUARTERS; + return benefitAmount * employeeCount * quartersLeft; + } + return 0; + })(), + }; +} diff --git a/app/home/[account]/_lib/server/load-team-account-benefit-statistics.ts b/app/home/[account]/_lib/server/load-team-account-benefit-statistics.ts new file mode 100644 index 0000000..2daa29e --- /dev/null +++ b/app/home/[account]/_lib/server/load-team-account-benefit-statistics.ts @@ -0,0 +1,113 @@ +'use server'; + +import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client'; + +export interface AccountBenefitStatistics { + benefitDistributionSchedule: { + amount: number; + }; + companyAccountsCount: number; + periodTotal: number; + orders: { + totalSum: number; + + analysesCount: number; + analysesSum: number; + + analysisPackagesCount: number; + analysisPackagesSum: number; + }; +} + +export const loadCompanyPersonalAccountsBalanceEntries = async ({ + accountId, +}: { + accountId: string; +}) => { + const supabase = getSupabaseServerAdminClient(); + + const { count, data: accountMemberships } = await supabase + .schema('medreport') + .from('accounts_memberships') + .select('user_id', { count: 'exact' }) + .eq('account_id', accountId) + .throwOnError(); + + const { data: accountBalanceEntries } = await supabase + .schema('medreport') + .from('account_balance_entries') + .select('*') + .eq('is_active', true) + .in( + 'account_id', + accountMemberships.map(({ user_id }) => user_id), + ) + .throwOnError(); + + const purchaseEntries = accountBalanceEntries.filter( + ({ entry_type }) => entry_type === 'purchase', + ); + const analysesEntries = purchaseEntries.filter( + ({ is_analysis_order }) => is_analysis_order, + ); + const analysisPackagesEntries = purchaseEntries.filter( + ({ is_analysis_package_order }) => is_analysis_package_order, + ); + + return { + accountBalanceEntries, + analysesEntries, + analysisPackagesEntries, + companyAccountsCount: count || 0, + purchaseEntries, + purchaseEntriesTotal: purchaseEntries.reduce( + (acc, { amount }) => acc + Math.abs(amount || 0), + 0, + ), + }; +}; + +export const loadAccountBenefitStatistics = async ( + accountId: string, +): Promise => { + const supabase = getSupabaseServerAdminClient(); + + const { + analysesEntries, + analysisPackagesEntries, + companyAccountsCount, + purchaseEntriesTotal, + } = await loadCompanyPersonalAccountsBalanceEntries({ accountId }); + + const { data: benefitDistributionSchedule } = await supabase + .schema('medreport') + .from('benefit_distribution_schedule') + .select('*') + .eq('company_id', accountId) + .eq('is_active', true) + .single(); + + const scheduleAmount = benefitDistributionSchedule?.benefit_amount || 0; + return { + companyAccountsCount, + benefitDistributionSchedule: { + amount: benefitDistributionSchedule?.benefit_amount || 0, + }, + periodTotal: scheduleAmount * companyAccountsCount, + orders: { + totalSum: purchaseEntriesTotal, + + analysesCount: analysesEntries.length, + analysesSum: analysesEntries.reduce( + (acc, { amount }) => acc + Math.abs(amount || 0), + 0, + ), + + analysisPackagesCount: analysisPackagesEntries.length, + analysisPackagesSum: analysisPackagesEntries.reduce( + (acc, { amount }) => acc + Math.abs(amount || 0), + 0, + ), + }, + }; +}; diff --git a/app/home/[account]/_lib/server/load-team-account-health-details.ts b/app/home/[account]/_lib/server/load-team-account-health-details.ts index 1705770..0827e0c 100644 --- a/app/home/[account]/_lib/server/load-team-account-health-details.ts +++ b/app/home/[account]/_lib/server/load-team-account-health-details.ts @@ -4,6 +4,8 @@ import { Database } from '@/packages/supabase/src/database.types'; import Isikukood from 'isikukood'; import { Clock, TrendingUp, User } from 'lucide-react'; +import type { BmiThresholds } from '@kit/accounts/types/accounts'; + import { bmiFromMetric, getBmiBackgroundColor, @@ -25,10 +27,7 @@ interface AccountHealthDetailsField { export const getAccountHealthDetailsFields = ( memberParams: TeamAccountStatisticsProps['memberParams'], - bmiThresholds: Omit< - Database['medreport']['Tables']['bmi_thresholds']['Row'], - 'id' - >[], + bmiThresholds: Omit[], members: Database['medreport']['Functions']['get_account_members']['Returns'], ): AccountHealthDetailsField[] => { const averageWeight = @@ -64,13 +63,13 @@ export const getAccountHealthDetailsFields = ( return [ { title: 'teams:healthDetails.women', - value: `${femalePercentage}% (${numberOfFemaleMembers})`, + value: `${femalePercentage.toFixed(0)}% (${numberOfFemaleMembers})`, Icon: User, iconBg: 'bg-success', }, { title: 'teams:healthDetails.men', - value: `${malePercentage}% (${numberOfMaleMembers})`, + value: `${malePercentage.toFixed(0)}% (${numberOfMaleMembers})`, Icon: User, iconBg: 'bg-success', }, @@ -82,7 +81,7 @@ export const getAccountHealthDetailsFields = ( }, { title: 'teams:healthDetails.bmi', - value: averageBMI, + value: averageBMI!, Icon: TrendingUp, iconBg: getBmiBackgroundColor(bmiStatus), }, diff --git a/app/home/[account]/billing/_components/health-benefit-form-client.tsx b/app/home/[account]/billing/_components/health-benefit-form-client.tsx new file mode 100644 index 0000000..a644e3b --- /dev/null +++ b/app/home/[account]/billing/_components/health-benefit-form-client.tsx @@ -0,0 +1,102 @@ +'use client'; + +import { useState } from 'react'; + +import { useRouter } from 'next/navigation'; + +import { UpdateHealthBenefitSchema } from '@/packages/billing/core/src/schema'; +import { + Account, + CompanyParams, +} from '@/packages/features/accounts/src/types/accounts'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +import { Button } from '@kit/ui/button'; +import { Form } from '@kit/ui/form'; +import { Spinner } from '@kit/ui/makerkit/spinner'; +import { toast } from '@kit/ui/shadcn/sonner'; +import { Trans } from '@kit/ui/trans'; + +import { cn } from '~/lib/utils'; + +import { updateHealthBenefit } from '../_lib/server/server-actions'; +import HealthBenefitFields from './health-benefit-fields'; + +const HealthBenefitFormClient = ({ + account, + companyParams, +}: { + account: Account; + companyParams: CompanyParams; +}) => { + const { t } = useTranslation('account'); + const router = useRouter(); + + const [currentCompanyParams, setCurrentCompanyParams] = + useState(companyParams); + const [isLoading, setIsLoading] = useState(false); + + const form = useForm({ + resolver: zodResolver(UpdateHealthBenefitSchema), + mode: 'onChange', + defaultValues: { + occurrence: currentCompanyParams.benefit_occurance || 'yearly', + amount: currentCompanyParams.benefit_amount || 0, + }, + }); + + const isDirty = form.formState.isDirty; + + const onSubmit = (data: { occurrence: string; amount: number }) => { + const promise = async () => { + setIsLoading(true); + try { + await updateHealthBenefit({ ...data, accountId: account.id }); + setCurrentCompanyParams((prev) => ({ + ...prev, + benefit_amount: data.amount, + benefit_occurance: data.occurrence, + })); + } finally { + form.reset(data); + setIsLoading(false); + router.refresh(); + } + }; + + toast.promise(promise, { + success: t('account:healthBenefitForm.updateSuccess'), + error: 'error', + }); + }; + + return ( +
+ + + + + + + ); +}; + +export default HealthBenefitFormClient; diff --git a/app/home/[account]/billing/_components/health-benefit-form.tsx b/app/home/[account]/billing/_components/health-benefit-form.tsx index a21a72e..84b58b7 100644 --- a/app/home/[account]/billing/_components/health-benefit-form.tsx +++ b/app/home/[account]/billing/_components/health-benefit-form.tsx @@ -1,138 +1,73 @@ -'use client'; - -import { useState } from 'react'; - -import { UpdateHealthBenefitSchema } from '@/packages/billing/core/src/schema'; -import { Database } from '@/packages/supabase/src/database.types'; -import { zodResolver } from '@hookform/resolvers/zod'; +import { + Account, + CompanyParams, +} from '@/packages/features/accounts/src/types/accounts'; import { PiggyBankIcon } from 'lucide-react'; -import { useForm } from 'react-hook-form'; -import { Button } from '@kit/ui/button'; -import { Form } from '@kit/ui/form'; -import { Spinner } from '@kit/ui/makerkit/spinner'; import { Separator } from '@kit/ui/shadcn/separator'; -import { toast } from '@kit/ui/shadcn/sonner'; import { Trans } from '@kit/ui/trans'; -import { cn } from '~/lib/utils'; - -import { updateHealthBenefit } from '../_lib/server/server-actions'; -import HealthBenefitFields from './health-benefit-fields'; +import { TeamAccountBenefitExpensesOverview } from '../../_lib/server/load-team-account-benefit-expenses-overview'; +import HealthBenefitFormClient from './health-benefit-form-client'; import YearlyExpensesOverview from './yearly-expenses-overview'; -const HealthBenefitForm = ({ +const HealthBenefitForm = async ({ account, companyParams, employeeCount, + expensesOverview, }: { - account: Database['medreport']['Tables']['accounts']['Row']; - companyParams: Database['medreport']['Tables']['company_params']['Row']; + account: Account; + companyParams: CompanyParams; employeeCount: number; + expensesOverview: TeamAccountBenefitExpensesOverview; }) => { - const [currentCompanyParams, setCurrentCompanyParams] = - useState( - companyParams, - ); - const [isLoading, setIsLoading] = useState(false); - const form = useForm({ - resolver: zodResolver(UpdateHealthBenefitSchema), - mode: 'onChange', - defaultValues: { - occurrence: currentCompanyParams.benefit_occurance || 'yearly', - amount: currentCompanyParams.benefit_amount || 0, - }, - }); - const isDirty = form.formState.isDirty; - - const onSubmit = (data: { occurrence: string; amount: number }) => { - const promise = async () => { - setIsLoading(true); - try { - await updateHealthBenefit({ ...data, accountId: account.id }); - setCurrentCompanyParams((prev) => ({ - ...prev, - benefit_amount: data.amount, - benefit_occurance: data.occurrence, - })); - } finally { - form.reset(data); - setIsLoading(false); - } - }; - - toast.promise(promise, { - success: 'Andmed uuendatud', - error: 'error', - }); - }; - return ( -
- -
-
-

- -

-

- -

-
- -
-
-
-
-
- -
-

- -

-

- {currentCompanyParams.benefit_amount || 0} € -

-
- - - -
- -
-
- -
- +
+
+

+ -

- +

+
+
+ +
+
+
+
+ +
+

+

+ + + +
+ +
- - + +
+ + +

+ +

+
+
+
); }; diff --git a/app/home/[account]/billing/_components/yearly-expenses-overview.tsx b/app/home/[account]/billing/_components/yearly-expenses-overview.tsx index 36a42be..232bf57 100644 --- a/app/home/[account]/billing/_components/yearly-expenses-overview.tsx +++ b/app/home/[account]/billing/_components/yearly-expenses-overview.tsx @@ -1,50 +1,23 @@ -import { useMemo } from 'react'; +'use client'; -import { Database } from '@/packages/supabase/src/database.types'; +import { formatCurrency } from '@/packages/shared/src/utils'; +import { useTranslation } from 'react-i18next'; import { Trans } from '@kit/ui/makerkit/trans'; import { Separator } from '@kit/ui/separator'; +import { TeamAccountBenefitExpensesOverview } from '../../_lib/server/load-team-account-benefit-expenses-overview'; + const YearlyExpensesOverview = ({ employeeCount = 0, - companyParams, + expensesOverview, }: { employeeCount?: number; - companyParams: Database['medreport']['Tables']['company_params']['Row']; + expensesOverview: TeamAccountBenefitExpensesOverview; }) => { - const monthlyExpensePerEmployee = useMemo(() => { - if (!companyParams.benefit_amount) { - return '0.00'; - } - - switch (companyParams.benefit_occurance) { - case 'yearly': - return (companyParams.benefit_amount / 12).toFixed(2); - case 'quarterly': - return (companyParams.benefit_amount / 3).toFixed(2); - case 'monthly': - return companyParams.benefit_amount.toFixed(2); - default: - return '0.00'; - } - }, [companyParams]); - - const maxYearlyExpensePerEmployee = useMemo(() => { - if (!companyParams.benefit_amount) { - return '0.00'; - } - - switch (companyParams.benefit_occurance) { - case 'yearly': - return companyParams.benefit_amount.toFixed(2); - case 'quarterly': - return (companyParams.benefit_amount * 3).toFixed(2); - case 'monthly': - return (companyParams.benefit_amount * 12).toFixed(2); - default: - return '0.00'; - } - }, [companyParams]); + const { + i18n: { language }, + } = useTranslation(); return (
@@ -53,41 +26,54 @@ const YearlyExpensesOverview = ({

- +

- - {monthlyExpensePerEmployee} € - + {employeeCount}
-

- -

- - {maxYearlyExpensePerEmployee} € - -
-

- {(Number(maxYearlyExpensePerEmployee) * employeeCount).toFixed(2)} € + {formatCurrency({ + value: expensesOverview.managementFeeTotal, + locale: language, + currencyCode: 'EUR', + })} + +
+
+

+ +

+ + {formatCurrency({ + value: expensesOverview.currentMonthUsageTotal, + locale: language, + currencyCode: 'EUR', + })}

- +

- {companyParams.benefit_amount - ? companyParams.benefit_amount * employeeCount - : 0}{' '} - € + {formatCurrency({ + value: expensesOverview.total, + locale: language, + currencyCode: 'EUR', + })}
diff --git a/app/home/[account]/billing/page.tsx b/app/home/[account]/billing/page.tsx index bc8288b..e3ca767 100644 --- a/app/home/[account]/billing/page.tsx +++ b/app/home/[account]/billing/page.tsx @@ -6,6 +6,7 @@ import { PageBody } from '@kit/ui/page'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; +import { loadTeamAccountBenefitExpensesOverview } from '../_lib/server/load-team-account-benefit-expenses-overview'; import HealthBenefitForm from './_components/health-benefit-form'; interface TeamAccountBillingPageProps { @@ -27,8 +28,14 @@ async function TeamAccountBillingPage({ params }: TeamAccountBillingPageProps) { const api = createTeamAccountsApi(client); const account = await api.getTeamAccount(accountSlug); - const companyParams = await api.getTeamAccountParams(account.id); const { members } = await api.getMembers(accountSlug); + const [expensesOverview, companyParams] = await Promise.all([ + loadTeamAccountBenefitExpensesOverview({ + companyId: account.id, + employeeCount: members.length, + }), + api.getTeamAccountParams(account.id), + ]); return ( @@ -36,6 +43,7 @@ async function TeamAccountBillingPage({ params }: TeamAccountBillingPageProps) { account={account} companyParams={companyParams} employeeCount={members.length} + expensesOverview={expensesOverview} /> ); diff --git a/app/home/[account]/members/_lib/server/members-page.loader.ts b/app/home/[account]/members/_lib/server/members-page.loader.ts index 6025aba..8df60c8 100644 --- a/app/home/[account]/members/_lib/server/members-page.loader.ts +++ b/app/home/[account]/members/_lib/server/members-page.loader.ts @@ -2,6 +2,7 @@ import 'server-only'; import { SupabaseClient } from '@supabase/supabase-js'; +import { getSupabaseServerAdminClient } from '@/packages/supabase/src/clients/server-admin-client'; import { Database } from '@/packages/supabase/src/database.types'; import { loadTeamWorkspace } from '~/home/[account]/_lib/server/team-account-workspace.loader'; @@ -15,11 +16,16 @@ export async function loadMembersPageData( client: SupabaseClient, slug: string, ) { + const workspace = await loadTeamWorkspace(slug); return Promise.all([ loadAccountMembers(client, slug), loadInvitations(client, slug), canAddMember, - loadTeamWorkspace(slug), + workspace, + loadAccountMembersBenefitsUsage( + getSupabaseServerAdminClient(), + workspace.account.id, + ), ]); } @@ -60,6 +66,29 @@ async function loadAccountMembers( return data ?? []; } +export async function loadAccountMembersBenefitsUsage( + client: SupabaseClient, + accountId: string, +): Promise< + { + personal_account_id: string; + benefit_amount: number; + }[] +> { + const { data, error } = await client + .schema('medreport') + .rpc('get_benefits_usages_for_company_members', { + p_account_id: accountId, + }); + + if (error) { + console.error('Failed to load account members benefits usage', error); + return []; + } + + return data ?? []; +} + /** * Load account invitations * @param client diff --git a/app/home/[account]/members/page.tsx b/app/home/[account]/members/page.tsx index a859654..eeb012e 100644 --- a/app/home/[account]/members/page.tsx +++ b/app/home/[account]/members/page.tsx @@ -42,8 +42,13 @@ async function TeamAccountMembersPage({ params }: TeamAccountMembersPageProps) { const client = getSupabaseServerClient(); const slug = (await params).account; - const [members, invitations, canAddMember, { user, account }] = - await loadMembersPageData(client, slug); + const [ + members, + invitations, + canAddMember, + { user, account }, + membersBenefitsUsage, + ] = await loadMembersPageData(client, slug); const canManageRoles = account.permissions.includes('roles.manage'); const canManageInvitations = account.permissions.includes('invites.manage'); @@ -54,7 +59,7 @@ async function TeamAccountMembersPage({ params }: TeamAccountMembersPageProps) { return ( <> } + title={} description={ } @@ -98,6 +103,7 @@ async function TeamAccountMembersPage({ params }: TeamAccountMembersPageProps) { members={members} isPrimaryOwner={isPrimaryOwner} canManageRoles={canManageRoles} + membersBenefitsUsage={membersBenefitsUsage} /> diff --git a/app/home/[account]/page.tsx b/app/home/[account]/page.tsx index 7283798..f53a1a8 100644 --- a/app/home/[account]/page.tsx +++ b/app/home/[account]/page.tsx @@ -17,6 +17,8 @@ import { } from '~/lib/services/audit/pageView.service'; import { Dashboard } from './_components/dashboard'; +import { loadTeamAccountBenefitExpensesOverview } from './_lib/server/load-team-account-benefit-expenses-overview'; +import { loadAccountBenefitStatistics } from './_lib/server/load-team-account-benefit-statistics'; interface TeamAccountHomePageProps { params: Promise<{ account: string }>; @@ -39,8 +41,14 @@ function TeamAccountHomePage({ params }: TeamAccountHomePageProps) { const teamAccount = use(teamAccountsApi.getTeamAccount(account)); const { memberParams, members } = use(teamAccountsApi.getMembers(account)); const bmiThresholds = use(userAnalysesApi.fetchBmiThresholds()); - const companyParams = use( - teamAccountsApi.getTeamAccountParams(teamAccount.id), + const accountBenefitStatistics = use( + loadAccountBenefitStatistics(teamAccount.id), + ); + const expensesOverview = use( + loadTeamAccountBenefitExpensesOverview({ + companyId: teamAccount.id, + employeeCount: members.length, + }), ); use( @@ -57,7 +65,8 @@ function TeamAccountHomePage({ params }: TeamAccountHomePageProps) { memberParams={memberParams} bmiThresholds={bmiThresholds} members={members} - companyParams={companyParams} + accountBenefitStatistics={accountBenefitStatistics} + expensesOverview={expensesOverview} /> ); diff --git a/app/join/page.tsx b/app/join/page.tsx index ec32148..cabd06b 100644 --- a/app/join/page.tsx +++ b/app/join/page.tsx @@ -17,6 +17,7 @@ import { Trans } from '@kit/ui/trans'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; +import { toTitleCase } from '~/lib/utils'; interface JoinTeamAccountPageProps { searchParams: Promise<{ @@ -110,12 +111,12 @@ async function JoinTeamAccountPage(props: JoinTeamAccountPageProps) { // once the user accepts the invitation, we redirect them to the account home page const membershipConfirmation = pathsConfig.auth.membershipConfirmation; - const email = auth.data.email ?? ''; + const fullName = toTitleCase(auth.data.user_metadata.full_name ?? ''); return ( .min(1), }) .parse({ + // Use for local testing medusaBackendPublicUrl: 'http://webhook.site:3000', siteUrl: 'http://webhook.site:3000', + // medusaBackendPublicUrl: process.env.MEDUSA_BACKEND_PUBLIC_URL!, + // siteUrl: process.env.NEXT_PUBLIC_SITE_URL!, }); export async function handleAddToCart({ @@ -42,6 +45,10 @@ export async function handleAddToCart({ selectedVariant: Pick; countryCode: string; }) { + try { + } catch (e) { + console.error('medusa card error: ', e); + } const { account } = await loadCurrentUserAccount(); if (!account) { throw new Error('Account not found'); @@ -85,9 +92,15 @@ export async function handleDeleteCartItem({ lineId }: { lineId: string }) { export async function handleNavigateToPayment({ language, paymentSessionId, + amount, + currencyCode, + cartId, }: { language: string; paymentSessionId: string; + amount: number; + currencyCode: string; + cartId: string; }) { const { account } = await loadCurrentUserAccount(); if (!account) { @@ -130,11 +143,11 @@ export async function handleNavigateToPayment({ await new MontonioOrderHandlerService().getMontonioPaymentLink({ notificationUrl: `${env().medusaBackendPublicUrl}/hooks/payment/montonio_montonio`, returnUrl: `${env().siteUrl}/home/cart/montonio-callback`, - amount: cart.total, - currency: cart.currency_code.toUpperCase(), + amount, + currency: currencyCode.toUpperCase(), description: `Order from Medreport`, locale: language, - merchantReference: `${account.id}:${paymentSessionId}:${cart.id}`, + merchantReference: `${account.id}:${paymentSessionId}:${cartId}`, }); await createCartEntriesLog({ diff --git a/lib/services/order.service.ts b/lib/services/order.service.ts index ee35de6..66ca175 100644 --- a/lib/services/order.service.ts +++ b/lib/services/order.service.ts @@ -51,48 +51,6 @@ export async function createAnalysisOrder({ return orderResult.data.id; } -export async function updateAnalysisOrder({ - orderId, - orderStatus, -}: { - orderId: number; - orderStatus: Tables<{ schema: 'medreport' }, 'analysis_orders'>['status']; -}) { - console.info(`Updating order id=${orderId} status=${orderStatus}`); - await getSupabaseServerAdminClient() - .schema('medreport') - .from('analysis_orders') - .update({ - status: orderStatus, - }) - .eq('id', orderId) - .throwOnError(); -} - -export async function updateAnalysisOrderStatus({ - orderId, - medusaOrderId, - orderStatus, -}: { - orderId?: number; - medusaOrderId?: string; - orderStatus: Tables<{ schema: 'medreport' }, 'analysis_orders'>['status']; -}) { - const orderIdParam = orderId; - const medusaOrderIdParam = medusaOrderId; - if (!orderIdParam && !medusaOrderIdParam) { - throw new Error('Either orderId or medusaOrderId must be provided'); - } - await getSupabaseServerAdminClient() - .schema('medreport') - .rpc('update_analysis_order_status', { - order_id: orderIdParam ?? -1, - status_param: orderStatus, - medusa_order_id_param: medusaOrderIdParam ?? '', - }) - .throwOnError(); -} - export async function getAnalysisOrder({ medusaOrderId, analysisOrderId, diff --git a/lib/utils.ts b/lib/utils.ts index 9feb9a5..841f57b 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,8 +1,9 @@ -import { Database } from '@/packages/supabase/src/database.types'; import { type ClassValue, clsx } from 'clsx'; import Isikukood, { Gender } from 'isikukood'; import { twMerge } from 'tailwind-merge'; +import type { BmiThresholds } from '@kit/accounts/types/accounts'; + import { BmiCategory } from './types/bmi'; export function cn(...inputs: ClassValue[]) { @@ -45,10 +46,7 @@ export const bmiFromMetric = (kg: number, cm: number) => { }; export function getBmiStatus( - thresholds: Omit< - Database['medreport']['Tables']['bmi_thresholds']['Row'], - 'id' - >[], + thresholds: Omit[], params: { age: number; height: number; weight: number }, ): BmiCategory | null { const age = params.age; diff --git a/packages/billing/montonio/src/services/montonio-webhook-handler.service.ts b/packages/billing/montonio/src/services/montonio-webhook-handler.service.ts index 97a2fd0..2df2be2 100644 --- a/packages/billing/montonio/src/services/montonio-webhook-handler.service.ts +++ b/packages/billing/montonio/src/services/montonio-webhook-handler.service.ts @@ -37,10 +37,11 @@ interface MontonioOrderToken { exp: number; } -const { secretKey } = MontonioServerEnvSchema.parse({ - apiUrl: process.env.MONTONIO_API_URL, - secretKey: process.env.MONTONIO_SECRET_KEY, -}); +const env = () => + MontonioServerEnvSchema.parse({ + apiUrl: process.env.MONTONIO_API_URL, + secretKey: process.env.MONTONIO_SECRET_KEY, + }); export class MontonioWebhookHandlerService implements BillingWebhookHandlerService @@ -50,6 +51,7 @@ export class MontonioWebhookHandlerService async verifyWebhookSignature(request: Request) { const logger = await getLogger(); + const { secretKey } = env(); let token: string; try { diff --git a/packages/database-webhooks/src/server/services/database-webhook-router.service.ts b/packages/database-webhooks/src/server/services/database-webhook-router.service.ts index 9299ae1..0cf8590 100644 --- a/packages/database-webhooks/src/server/services/database-webhook-router.service.ts +++ b/packages/database-webhooks/src/server/services/database-webhook-router.service.ts @@ -48,6 +48,12 @@ class DatabaseWebhookRouterService { return this.handleAnalysisOrdersWebhook(payload); } + case 'connected_online_reservation': { + const payload = body as RecordChange; + + return this.handleTtoOrdersWebhook(payload); + } + default: { return; } @@ -100,13 +106,27 @@ class DatabaseWebhookRouterService { return; } - const { createAnalysisOrderWebhooksService } = await import( - '@kit/notifications/webhooks/analysis-order-notifications.service' + const { createOrderWebhooksService } = await import( + '@kit/notifications/webhooks/order-notifications.service' ); - const service = createAnalysisOrderWebhooksService(); + const service = createOrderWebhooksService(); return service.handleStatusChangeWebhook(record); } } + + private async handleTtoOrdersWebhook( + body: RecordChange<'connected_online_reservation'>, + ) { + if (body.type === 'UPDATE' && body.record) { + const { createOrderWebhooksService } = await import( + '@kit/notifications/webhooks/order-notifications.service' + ); + + const service = createOrderWebhooksService(); + + return service.handleTtoReservationConfirmationWebhook(body.record); + } + } } diff --git a/packages/email-templates/package.json b/packages/email-templates/package.json index 9628ddb..f350452 100644 --- a/packages/email-templates/package.json +++ b/packages/email-templates/package.json @@ -4,17 +4,20 @@ "version": "0.1.0", "scripts": { "clean": "git clean -xdf .turbo node_modules", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "email:dev": "email dev --dir src/emails --port 3001" }, "exports": { ".": "./src/index.ts" }, "dependencies": { - "@react-email/components": "0.0.41" + "@react-email/components": "0.0.41", + "react-email": "4.2.12" }, "devDependencies": { "@kit/i18n": "workspace:*", - "@kit/tsconfig": "workspace:*" + "@kit/tsconfig": "workspace:*", + "@react-email/preview-server": "4.2.12" }, "typesVersions": { "*": { diff --git a/packages/email-templates/src/components/common-footer.tsx b/packages/email-templates/src/components/common-footer.tsx index 32480ee..8214f1b 100644 --- a/packages/email-templates/src/components/common-footer.tsx +++ b/packages/email-templates/src/components/common-footer.tsx @@ -6,22 +6,28 @@ import { EmailFooter } from './footer'; export default function CommonFooter({ t }: { t: TFunction }) { const namespace = 'common'; - const lines = [ - t(`${namespace}:footer.lines1`), - t(`${namespace}:footer.lines2`), - t(`${namespace}:footer.lines3`), - t(`${namespace}:footer.lines4`), - ]; - return ( - {lines.map((line, index) => ( - - ))} + + {t(`${namespace}:footer.title`)} + + + {t(`${namespace}:footer.emailField`)}{' '} + + {t(`${namespace}:footer.email`)} + + + + {t(`${namespace}:footer.phoneField`)}{' '} + + {t(`${namespace}:footer.phone`)} + + + + + {t(`${namespace}:footer.website`)} + + ); } diff --git a/packages/email-templates/src/components/footer.tsx b/packages/email-templates/src/components/footer.tsx index c201795..5c97e09 100644 --- a/packages/email-templates/src/components/footer.tsx +++ b/packages/email-templates/src/components/footer.tsx @@ -1,11 +1,11 @@ -import { Container, Text } from '@react-email/components'; +import { Container, Section } from '@react-email/components'; export function EmailFooter(props: React.PropsWithChildren) { return ( - +
{props.children} - +
); } diff --git a/packages/email-templates/src/emails/email.tsx b/packages/email-templates/src/emails/email.tsx new file mode 100644 index 0000000..84e2cd4 --- /dev/null +++ b/packages/email-templates/src/emails/email.tsx @@ -0,0 +1,8 @@ +import React from 'react'; + +const Email = () => { + return
Email
; +}; + +export default Email; +Email.PreviewProps = {}; diff --git a/packages/email-templates/src/emails/tto-reservation-confirmation.email.tsx b/packages/email-templates/src/emails/tto-reservation-confirmation.email.tsx new file mode 100644 index 0000000..e8dadc3 --- /dev/null +++ b/packages/email-templates/src/emails/tto-reservation-confirmation.email.tsx @@ -0,0 +1,137 @@ +import { + Body, + Head, + Html, + Link, + Preview, + Tailwind, + Text, + render, +} from '@react-email/components'; + +import { BodyStyle } from '../components/body-style'; +import CommonFooter from '../components/common-footer'; +import { EmailContent } from '../components/content'; +import { EmailHeader } from '../components/header'; +import { EmailHeading } from '../components/heading'; +import { EmailWrapper } from '../components/wrapper'; +import { initializeEmailI18n } from '../lib/i18n'; + +export async function renderTtoReservationConfirmationEmail({ + language, + recipientName, + startTime, + orderName, + locationName, + locationAddress, + orderId, + serviceProviderName, + serviceProviderEmail, + serviceProviderPhone, +}: { + language: string; + recipientName: string; + startTime: string; + orderName: string; + locationName?: string; + locationAddress?: string | null; + orderId: string; + serviceProviderName?: string; + serviceProviderEmail?: string | null; + serviceProviderPhone?: string | null; +}) { + const namespace = 'tto-reservation-confirmation-email'; + + const { t } = await initializeEmailI18n({ + language, + namespace: [namespace, 'common'], + }); + + const previewText = t(`${namespace}:previewText`, { + reservation: orderName, + }); + + const subject = t(`${namespace}:subject`, { + reservation: orderName, + }); + + const email = ( + + + + + + {previewText} + + + + + + + {previewText} + + + {t(`${namespace}:helloName`, { name: recipientName })} + + + {t(`${namespace}:thankYou`)} + + + {orderName}, {new Date(startTime).toLocaleString()},{' '} + {locationAddress}, {locationName} + + + + {t(`${namespace}:viewOrder`)} + + + + {serviceProviderName} + + + {t(`${namespace}:customerSupport`)} + + {serviceProviderPhone} + + + + {serviceProviderEmail} + + + + + + + + + ); + + const html = await render(email); + + return { + html, + subject, + email, + }; +} + +const PreviewEmail = async () => { + const { email } = await renderTtoReservationConfirmationEmail({ + language: 'et', + recipientName: 'John Doe', + startTime: '2025-09-27 05:45:00+00', + orderName: 'Hambaarst', + locationName: 'Tallinn', + locationAddress: 'Põhja puiestee, 2/3A', + orderId: '123123', + serviceProviderName: 'Dentas OÜ', + serviceProviderEmail: 'email@example.ee', + serviceProviderPhone: '+372111111', + }); + return email; +}; + +export default PreviewEmail; +PreviewEmail.PreviewProps = {}; diff --git a/packages/email-templates/src/index.ts b/packages/email-templates/src/index.ts index 669a84b..059235e 100644 --- a/packages/email-templates/src/index.ts +++ b/packages/email-templates/src/index.ts @@ -11,3 +11,4 @@ export * from './emails/order-processing.email'; export * from './emails/patient-first-results-received.email'; export * from './emails/patient-full-results-received.email'; export * from './emails/book-time-failed.email'; +export * from './emails/tto-reservation-confirmation.email'; diff --git a/packages/email-templates/src/locales/en/tto-reservation-confirmation-email.json b/packages/email-templates/src/locales/en/tto-reservation-confirmation-email.json new file mode 100644 index 0000000..830822f --- /dev/null +++ b/packages/email-templates/src/locales/en/tto-reservation-confirmation-email.json @@ -0,0 +1,8 @@ +{ + "previewText": "Your booking is confirmed! - {{reservation}}", + "subject": "Your booking is confirmed! - {{reservation}}", + "helloName": "Hello, {{name}}!", + "thankYou": "Thank you for your order!", + "viewOrder": "See booking", + "customerSupport": "Customer support: " +} diff --git a/packages/email-templates/src/locales/et/common.json b/packages/email-templates/src/locales/et/common.json index c663ccf..38349b2 100644 --- a/packages/email-templates/src/locales/et/common.json +++ b/packages/email-templates/src/locales/et/common.json @@ -1,9 +1,11 @@ { "footer": { - "lines1": "MedReport", - "lines2": "E-mail: info@medreport.ee", - "lines3": "Klienditugi: +372 5887 1517", - "lines4": "www.medreport.ee" + "title": "MedReport", + "emailField": "E-mail:", + "email": "info@medreport.ee", + "phoneField": "Klienditugi:", + "phone": "+372 5887 1517", + "website": "www.medreport.ee" }, "helloName": "Tere, {{name}}", "hello": "Tere" diff --git a/packages/email-templates/src/locales/et/tto-reservation-confirmation-email.json b/packages/email-templates/src/locales/et/tto-reservation-confirmation-email.json new file mode 100644 index 0000000..fad1006 --- /dev/null +++ b/packages/email-templates/src/locales/et/tto-reservation-confirmation-email.json @@ -0,0 +1,8 @@ +{ + "previewText": "Teie broneering on kinnitatud! - {{reservation}}", + "subject": "Teie broneering on kinnitatud! - {{reservation}}", + "helloName": "Tere, {{name}}!", + "thankYou": "Täname tellimuse eest!", + "viewOrder": "Vaata broneeringut", + "customerSupport": "Klienditugi: " +} diff --git a/packages/email-templates/src/locales/ru/tto-reservation-confirmation-email.json b/packages/email-templates/src/locales/ru/tto-reservation-confirmation-email.json new file mode 100644 index 0000000..830822f --- /dev/null +++ b/packages/email-templates/src/locales/ru/tto-reservation-confirmation-email.json @@ -0,0 +1,8 @@ +{ + "previewText": "Your booking is confirmed! - {{reservation}}", + "subject": "Your booking is confirmed! - {{reservation}}", + "helloName": "Hello, {{name}}!", + "thankYou": "Thank you for your order!", + "viewOrder": "See booking", + "customerSupport": "Customer support: " +} diff --git a/packages/features/accounts/package.json b/packages/features/accounts/package.json index e75f6b5..9385881 100644 --- a/packages/features/accounts/package.json +++ b/packages/features/accounts/package.json @@ -12,6 +12,7 @@ "./personal-account-settings": "./src/components/personal-account-settings/index.ts", "./components": "./src/components/index.ts", "./hooks/*": "./src/hooks/*.ts", + "./services/*": "./src/server/services/*.ts", "./api": "./src/server/api.ts", "./types/*": "./src/types/*.ts" }, diff --git a/packages/features/accounts/src/server/services/account-balance.service.ts b/packages/features/accounts/src/server/services/account-balance.service.ts new file mode 100644 index 0000000..8a7f1c5 --- /dev/null +++ b/packages/features/accounts/src/server/services/account-balance.service.ts @@ -0,0 +1,134 @@ +import { getSupabaseServerClient } from '@kit/supabase/server-client'; + +import type { AccountBalanceEntry } from '../../types/account-balance-entry'; + +export type AccountBalanceSummary = { + totalBalance: number; + expiringSoon: number; + recentEntries: AccountBalanceEntry[]; +}; + +export class AccountBalanceService { + private supabase: ReturnType; + + constructor() { + this.supabase = getSupabaseServerClient(); + } + + /** + * Get the current balance for a specific account + */ + async getAccountBalance(accountId: string): Promise { + const { data, error } = await this.supabase + .schema('medreport') + .rpc('get_account_balance', { + p_account_id: accountId, + }); + + if (error) { + console.error('Error getting account balance:', error); + throw new Error('Failed to get account balance'); + } + + return data || 0; + } + + /** + * Get balance entries for an account with pagination + */ + async getAccountBalanceEntries( + accountId: string, + options: { + limit?: number; + offset?: number; + entryType?: string; + includeInactive?: boolean; + } = {}, + ): Promise<{ + entries: AccountBalanceEntry[]; + total: number; + }> { + const { + limit = 50, + offset = 0, + entryType, + includeInactive = false, + } = options; + + let query = this.supabase + .schema('medreport') + .from('account_balance_entries') + .select('*', { count: 'exact' }) + .eq('account_id', accountId) + .order('created_at', { ascending: false }) + .range(offset, offset + limit - 1); + + if (!includeInactive) { + query = query.eq('is_active', true); + } + + if (entryType) { + query = query.eq('entry_type', entryType); + } + + const { data, error, count } = await query; + + if (error) { + console.error('Error getting account balance entries:', error); + throw new Error('Failed to get account balance entries'); + } + + return { + entries: data || [], + total: count || 0, + }; + } + + /** + * Get balance summary for dashboard display + */ + async getBalanceSummary(accountId: string): Promise { + const [balance, entries] = await Promise.all([ + this.getAccountBalance(accountId), + this.getAccountBalanceEntries(accountId, { limit: 5 }), + ]); + + // Calculate expiring balance (next 30 days) + const thirtyDaysFromNow = new Date(); + thirtyDaysFromNow.setDate(thirtyDaysFromNow.getDate() + 30); + + const { data: expiringData, error: expiringError } = await this.supabase + .schema('medreport') + .from('account_balance_entries') + .select('amount') + .eq('account_id', accountId) + .eq('is_active', true) + .not('expires_at', 'is', null) + .lte('expires_at', thirtyDaysFromNow.toISOString()); + + if (expiringError) { + console.error('Error getting expiring balance:', expiringError); + } + + const expiringSoon = + expiringData?.reduce((sum, entry) => sum + (entry.amount || 0), 0) || 0; + + return { + totalBalance: balance, + expiringSoon, + recentEntries: entries.entries, + }; + } + + async processPeriodicBenefitDistributions(): Promise { + console.info('Processing periodic benefit distributions...'); + const { error } = await this.supabase + .schema('medreport') + .rpc('process_periodic_benefit_distributions'); + if (error) { + console.error('Error processing periodic benefit distributions:', error); + throw new Error('Failed to process periodic benefit distributions'); + } + console.info('Periodic benefit distributions processed successfully'); + } +} diff --git a/packages/features/accounts/src/types/account-balance-entry.ts b/packages/features/accounts/src/types/account-balance-entry.ts new file mode 100644 index 0000000..e58f853 --- /dev/null +++ b/packages/features/accounts/src/types/account-balance-entry.ts @@ -0,0 +1,4 @@ +import type { Database } from '@kit/supabase/database'; + +export type AccountBalanceEntry = + Database['medreport']['Tables']['account_balance_entries']['Row']; diff --git a/packages/features/accounts/src/types/accounts.ts b/packages/features/accounts/src/types/accounts.ts index bc305c0..67866c1 100644 --- a/packages/features/accounts/src/types/accounts.ts +++ b/packages/features/accounts/src/types/accounts.ts @@ -1,23 +1,26 @@ import { Database } from '@kit/supabase/database'; -export type ApplicationRole = - Database['medreport']['Tables']['accounts']['Row']['application_role']; +export type ApplicationRole = Account['application_role']; export enum ApplicationRoleEnum { User = 'user', Doctor = 'doctor', SuperAdmin = 'super_admin', } -export type AccountWithParams = - Database['medreport']['Tables']['accounts']['Row'] & { - accountParams: - | (Pick< - Database['medreport']['Tables']['account_params']['Row'], - 'weight' | 'height' - > & { - isSmoker: - | Database['medreport']['Tables']['account_params']['Row']['is_smoker'] - | null; - }) - | null; - }; +export type AccountParams = + Database['medreport']['Tables']['account_params']['Row']; + +export type Account = Database['medreport']['Tables']['accounts']['Row']; +export type AccountWithParams = Account & { + accountParams: + | (Pick & { + isSmoker: AccountParams['is_smoker'] | null; + }) + | null; +}; + +export type CompanyParams = + Database['medreport']['Tables']['company_params']['Row']; + +export type BmiThresholds = + Database['medreport']['Tables']['bmi_thresholds']['Row']; diff --git a/packages/features/admin/package.json b/packages/features/admin/package.json index 72da143..d07673d 100644 --- a/packages/features/admin/package.json +++ b/packages/features/admin/package.json @@ -9,6 +9,7 @@ "devDependencies": { "@hookform/resolvers": "^5.0.1", "@kit/next": "workspace:*", + "@kit/accounts": "workspace:*", "@kit/shared": "workspace:*", "@kit/supabase": "workspace:*", "@kit/tsconfig": "workspace:*", diff --git a/packages/features/admin/src/components/admin-accounts-table.tsx b/packages/features/admin/src/components/admin-accounts-table.tsx index ab7f0c1..d993ab7 100644 --- a/packages/features/admin/src/components/admin-accounts-table.tsx +++ b/packages/features/admin/src/components/admin-accounts-table.tsx @@ -11,7 +11,7 @@ import { EllipsisVertical } from 'lucide-react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; -import { Database } from '@kit/supabase/database'; +import type { Account } from '@kit/accounts/types/accounts'; import { Button } from '@kit/ui/button'; import { Checkbox } from '@kit/ui/checkbox'; import { @@ -44,8 +44,6 @@ import { AdminDeleteUserDialog } from './admin-delete-user-dialog'; import { AdminImpersonateUserDialog } from './admin-impersonate-user-dialog'; import { AdminResetPasswordDialog } from './admin-reset-password-dialog'; -type Account = Database['medreport']['Tables']['accounts']['Row']; - const FiltersSchema = z.object({ type: z.enum(['all', 'team', 'personal']), query: z.string().optional(), diff --git a/packages/features/admin/src/lib/server/schema/admin-actions.schema.ts b/packages/features/admin/src/lib/server/schema/admin-actions.schema.ts index 8edd356..aa5d86d 100644 --- a/packages/features/admin/src/lib/server/schema/admin-actions.schema.ts +++ b/packages/features/admin/src/lib/server/schema/admin-actions.schema.ts @@ -1,6 +1,6 @@ import { z } from 'zod'; -import { Database } from '@kit/supabase/database'; +import { ApplicationRole } from '@kit/accounts/types/accounts'; const ConfirmationSchema = z.object({ confirmation: z.custom((value) => value === 'CONFIRM'), @@ -19,9 +19,7 @@ export const DeleteAccountSchema = ConfirmationSchema.extend({ accountId: z.string().uuid(), }); -type ApplicationRoleType = - Database['medreport']['Tables']['accounts']['Row']['application_role']; export const UpdateAccountRoleSchema = z.object({ accountId: z.string().uuid(), - role: z.string() as z.ZodType, + role: z.string() as z.ZodType, }); diff --git a/packages/features/admin/src/lib/server/services/admin-accounts.service.ts b/packages/features/admin/src/lib/server/services/admin-accounts.service.ts index c46bc03..3732bd4 100644 --- a/packages/features/admin/src/lib/server/services/admin-accounts.service.ts +++ b/packages/features/admin/src/lib/server/services/admin-accounts.service.ts @@ -2,6 +2,7 @@ import 'server-only'; import { SupabaseClient } from '@supabase/supabase-js'; +import type { ApplicationRole } from '@kit/accounts/types/accounts'; import { Database } from '@kit/supabase/database'; export function createAdminAccountsService(client: SupabaseClient) { @@ -23,10 +24,7 @@ class AdminAccountsService { } } - async updateRole( - accountId: string, - role: Database['medreport']['Tables']['accounts']['Row']['application_role'], - ) { + async updateRole(accountId: string, role: ApplicationRole) { const { error } = await this.adminClient .schema('medreport') .from('accounts') diff --git a/packages/features/doctor/package.json b/packages/features/doctor/package.json index df1c635..fdf8e74 100644 --- a/packages/features/doctor/package.json +++ b/packages/features/doctor/package.json @@ -13,6 +13,7 @@ "@kit/supabase": "workspace:*", "@kit/tsconfig": "workspace:*", "@kit/ui": "workspace:*", + "@kit/user-analyses": "workspace:*", "@makerkit/data-loader-supabase-core": "^0.0.10", "@makerkit/data-loader-supabase-nextjs": "^1.2.5", "@supabase/supabase-js": "2.49.4", diff --git a/packages/features/doctor/src/lib/server/services/doctor-analysis.service.ts b/packages/features/doctor/src/lib/server/services/doctor-analysis.service.ts index c835c76..7033567 100644 --- a/packages/features/doctor/src/lib/server/services/doctor-analysis.service.ts +++ b/packages/features/doctor/src/lib/server/services/doctor-analysis.service.ts @@ -5,6 +5,7 @@ import { isBefore } from 'date-fns'; import { renderDoctorSummaryReceivedEmail } from '@kit/email-templates'; import { getFullName } from '@kit/shared/utils'; import { getSupabaseServerClient } from '@kit/supabase/server-client'; +import { createUserAnalysesApi } from '@kit/user-analyses/api'; import { sendEmailFromTemplate } from '../../../../../../../lib/services/mailer.service'; import { AnalysisResultDetails } from '../schema/doctor-analysis-detail-view.schema'; @@ -641,7 +642,14 @@ export async function submitFeedback( } if (status === 'COMPLETED') { - const [{ data: recipient }, { data: analysisOrder }] = await Promise.all([ + const { data: analysisOrder } = await supabase + .schema('medreport') + .from('analysis_orders') + .select('medusa_order_id, id') + .eq('id', analysisOrderId) + .limit(1) + .throwOnError(); + const [{ data: recipient }] = await Promise.all([ supabase .schema('medreport') .from('accounts') @@ -649,19 +657,10 @@ export async function submitFeedback( .eq('is_personal_account', true) .eq('primary_owner_user_id', userId) .throwOnError(), - supabase - .schema('medreport') - .from('analysis_orders') - .select('medusa_order_id, id') - .eq('id', analysisOrderId) - .limit(1) - .throwOnError(), - supabase - .schema('medreport') - .from('analysis_orders') - .update({ status: 'COMPLETED' }) - .eq('id', analysisOrderId) - .throwOnError(), + createUserAnalysesApi(supabase).updateAnalysisOrderStatus({ + orderId: analysisOrderId, + orderStatus: 'COMPLETED', + }), ]); if (!recipient?.[0]?.email) { diff --git a/packages/features/medusa-storefront/src/lib/data/cart.ts b/packages/features/medusa-storefront/src/lib/data/cart.ts index b386b12..ddb9350 100644 --- a/packages/features/medusa-storefront/src/lib/data/cart.ts +++ b/packages/features/medusa-storefront/src/lib/data/cart.ts @@ -5,7 +5,7 @@ import { redirect } from 'next/navigation'; import { sdk } from '@lib/config'; import medusaError from '@lib/util/medusa-error'; -import { HttpTypes } from '@medusajs/types'; +import { HttpTypes, StoreCart } from '@medusajs/types'; import { getAuthHeaders, @@ -258,6 +258,42 @@ export async function setShippingMethod({ .catch(medusaError); } +export async function initiateMultiPaymentSession( + cart: StoreCart, + benefitsAmount: number, +) { + const headers = { + ...(await getAuthHeaders()), + }; + + return sdk.client + .fetch(`/store/multi-payment`, { + method: 'POST', + body: { cartId: cart.id, benefitsAmount }, + headers, + }) + .then(async (response) => { + console.info('Payment session initiated:', response); + const cartCacheTag = await getCacheTag('carts'); + revalidateTag(cartCacheTag); + return response as { + montonioPaymentSessionId: string | null; + companyBenefitsPaymentSessionId: string | null; + totalByBenefits: number; + totalByMontonio: number; + isFullyPaidByBenefits: boolean; + }; + }) + .catch((e) => { + console.error( + 'Error initiating payment session:', + e, + JSON.stringify(Object.keys(e)), + ); + return medusaError(e); + }); +} + export async function initiatePaymentSession( cart: HttpTypes.StoreCart, data: HttpTypes.StoreInitializePaymentSession, diff --git a/packages/features/medusa-storefront/src/lib/data/collections.ts b/packages/features/medusa-storefront/src/lib/data/collections.ts index 540e2f5..62cb856 100644 --- a/packages/features/medusa-storefront/src/lib/data/collections.ts +++ b/packages/features/medusa-storefront/src/lib/data/collections.ts @@ -30,7 +30,7 @@ export const listCollections = async ( queryParams.limit = queryParams.limit || '100'; queryParams.offset = queryParams.offset || '0'; - console.log('SDK_CONFIG: ', SDK_CONFIG.baseUrl); + return sdk.client .fetch<{ collections: HttpTypes.StoreCollection[]; count: number }>( '/store/collections', diff --git a/packages/features/medusa-storefront/src/lib/data/orders.ts b/packages/features/medusa-storefront/src/lib/data/orders.ts index 6473ab0..464a747 100644 --- a/packages/features/medusa-storefront/src/lib/data/orders.ts +++ b/packages/features/medusa-storefront/src/lib/data/orders.ts @@ -6,7 +6,7 @@ import { HttpTypes } from '@medusajs/types'; import { getAuthHeaders, getCacheOptions } from './cookies'; -export const retrieveOrder = async (id: string) => { +export const retrieveOrder = async (id: string, allowCache = true) => { const headers = { ...(await getAuthHeaders()), }; @@ -24,7 +24,8 @@ export const retrieveOrder = async (id: string) => { }, headers, next, - cache: 'force-cache', + ...(allowCache ? { cache: 'force-cache' } : {}), + credentials: 'include', }) .then(({ order }) => order) .catch((err) => medusaError(err)); @@ -55,6 +56,7 @@ export const listOrders = async ( }, headers, next, + credentials: 'include', }) .then(({ orders }) => orders) .catch((err) => medusaError(err)); diff --git a/packages/features/notifications/src/server/services/webhooks/analysis-order-notifications.service.ts b/packages/features/notifications/src/server/services/webhooks/order-notifications.service.ts similarity index 64% rename from packages/features/notifications/src/server/services/webhooks/analysis-order-notifications.service.ts rename to packages/features/notifications/src/server/services/webhooks/order-notifications.service.ts index 643b1b7..3adc4bb 100644 --- a/packages/features/notifications/src/server/services/webhooks/analysis-order-notifications.service.ts +++ b/packages/features/notifications/src/server/services/webhooks/order-notifications.service.ts @@ -1,9 +1,12 @@ +import { sdk } from '@lib/config'; + import { renderAllResultsReceivedEmail, renderFirstResultsReceivedEmail, renderOrderProcessingEmail, renderPatientFirstResultsReceivedEmail, renderPatientFullResultsReceivedEmail, + renderTtoReservationConfirmationEmail, } from '@kit/email-templates'; import { getLogger } from '@kit/shared/logger'; import { getFullName } from '@kit/shared/utils'; @@ -25,13 +28,15 @@ import { } from '~/lib/services/mailer.service'; type AnalysisOrder = Database['medreport']['Tables']['analysis_orders']['Row']; +type TtoReservation = + Database['medreport']['Tables']['connected_online_reservation']['Row']; -export function createAnalysisOrderWebhooksService() { - return new AnalysisOrderWebhooksService(); +export function createOrderWebhooksService() { + return new OrderWebhooksService(); } -class AnalysisOrderWebhooksService { - private readonly namespace = 'analysis_orders.webhooks'; +class OrderWebhooksService { + private readonly namespace = 'orders.webhooks'; async handleStatusChangeWebhook(analysisOrder: AnalysisOrder) { const logger = await getLogger(); @@ -90,6 +95,128 @@ class AnalysisOrderWebhooksService { } } + async handleTtoReservationConfirmationWebhook( + ttoReservation: TtoReservation, + ) { + const logger = await getLogger(); + const ctx = { + ttoReservationId: ttoReservation.id, + namespace: this.namespace, + }; + + try { + const userContact = await getUserContactAdmin(ttoReservation.user_id); + if (!userContact.email) { + await createNotificationLog({ + action: NotificationAction.TTO_ORDER_CONFIRMATION, + status: 'FAIL', + comment: 'No email found for ' + ttoReservation.user_id, + relatedRecordId: ttoReservation.id, + }); + logger.warn(ctx, 'No email found '); + return; + } + const supabaseClient = getSupabaseServerAdminClient(); + if (!ttoReservation.medusa_cart_line_item_id) { + await createNotificationLog({ + action: NotificationAction.TTO_ORDER_CONFIRMATION, + status: 'FAIL', + comment: 'No cart item id found for ' + ttoReservation.user_id, + relatedRecordId: ttoReservation.id, + }); + logger.warn(ctx, 'No cart item id found '); + return; + } + const [ + { data: cartItem }, + { data: location }, + { data: serviceProvider }, + ] = await Promise.all([ + supabaseClient + .from('cart_line_item') + .select('cart_id,title') + .eq('id', ttoReservation.medusa_cart_line_item_id) + .single(), + supabaseClient + .schema('medreport') + .from('connected_online_locations') + .select('name,address') + .eq('sync_id', ttoReservation.location_sync_id || 0) + .single(), + supabaseClient + .schema('medreport') + .from('connected_online_providers') + .select('email,phone_number,name') + .eq('id', ttoReservation.clinic_id) + .single(), + ]); + + if (!cartItem) { + await createNotificationLog({ + action: NotificationAction.TTO_ORDER_CONFIRMATION, + status: 'FAIL', + comment: 'No medusa cart item found for ' + ttoReservation.user_id, + relatedRecordId: ttoReservation.medusa_cart_line_item_id, + }); + logger.warn(ctx, 'No cart item found '); + return; + } + + const [{ data: orderCart }] = await Promise.all([ + supabaseClient + .from('order_cart') + .select('order_id') + .eq('cart_id', cartItem.cart_id) + .single(), + ]); + + if (!orderCart) { + await createNotificationLog({ + action: NotificationAction.TTO_ORDER_CONFIRMATION, + status: 'FAIL', + comment: 'No medusa order cart found for ' + ttoReservation.user_id, + relatedRecordId: cartItem.cart_id, + }); + logger.warn(ctx, 'No order cart found '); + return; + } + + await sendEmailFromTemplate( + renderTtoReservationConfirmationEmail, + { + language: userContact.preferred_locale ?? 'et', + recipientName: getFullName(userContact.name, userContact.last_name), + startTime: ttoReservation.start_time, + orderName: cartItem.title, + locationName: location?.name, + locationAddress: location?.address, + orderId: orderCart.order_id, + serviceProviderName: serviceProvider?.name, + serviceProviderEmail: serviceProvider?.email, + serviceProviderPhone: serviceProvider?.phone_number, + }, + userContact.email, + ); + + return createNotificationLog({ + action: NotificationAction.TTO_ORDER_CONFIRMATION, + status: 'SUCCESS', + relatedRecordId: orderCart.order_id, + }); + } catch (e: any) { + createNotificationLog({ + action: NotificationAction.TTO_ORDER_CONFIRMATION, + status: 'FAIL', + comment: e?.message, + relatedRecordId: ttoReservation.id, + }); + logger.error( + ctx, + `Error while processing tto reservation: ${JSON.stringify(e)}`, + ); + } + } + async sendProcessingNotification(analysisOrder: AnalysisOrder) { const logger = await getLogger(); const supabase = getSupabaseServerAdminClient(); diff --git a/packages/features/team-accounts/src/components/invitations/accept-invitation-container.tsx b/packages/features/team-accounts/src/components/invitations/accept-invitation-container.tsx index 0af6042..bcfc4d4 100644 --- a/packages/features/team-accounts/src/components/invitations/accept-invitation-container.tsx +++ b/packages/features/team-accounts/src/components/invitations/accept-invitation-container.tsx @@ -1,6 +1,5 @@ import Image from 'next/image'; -import { useDismissNotification } from '@kit/notifications/hooks'; import { Heading } from '@kit/ui/heading'; import { If } from '@kit/ui/if'; import { Separator } from '@kit/ui/separator'; @@ -12,7 +11,7 @@ import { SignOutInvitationButton } from './sign-out-invitation-button'; export function AcceptInvitationContainer(props: { inviteToken: string; - email: string; + fullName: string; invitation: { id: string; @@ -76,7 +75,7 @@ export function AcceptInvitationContainer(props: { /> diff --git a/packages/features/team-accounts/src/components/invitations/invitation-submit-button.tsx b/packages/features/team-accounts/src/components/invitations/invitation-submit-button.tsx index 17a74c5..54ede8b 100644 --- a/packages/features/team-accounts/src/components/invitations/invitation-submit-button.tsx +++ b/packages/features/team-accounts/src/components/invitations/invitation-submit-button.tsx @@ -7,7 +7,7 @@ import { Trans } from '@kit/ui/trans'; export function InvitationSubmitButton(props: { accountName: string; - email: string; + fullName: string; }) { const { pending } = useFormStatus(); @@ -17,7 +17,7 @@ export function InvitationSubmitButton(props: { i18nKey={pending ? 'teams:joiningTeam' : 'teams:continueAs'} values={{ accountName: props.accountName, - email: props.email, + fullName: props.fullName, }} /> diff --git a/packages/features/team-accounts/src/components/members/account-members-table.tsx b/packages/features/team-accounts/src/components/members/account-members-table.tsx index 0e461f9..d1fbcac 100644 --- a/packages/features/team-accounts/src/components/members/account-members-table.tsx +++ b/packages/features/team-accounts/src/components/members/account-members-table.tsx @@ -6,6 +6,7 @@ import { ColumnDef } from '@tanstack/react-table'; import { Ellipsis } from 'lucide-react'; import { useTranslation } from 'react-i18next'; +import { formatCurrency } from '@kit/shared/utils'; import { Database } from '@kit/supabase/database'; import { Badge } from '@kit/ui/badge'; import { Button } from '@kit/ui/button'; @@ -42,6 +43,10 @@ type AccountMembersTableProps = { userRoleHierarchy: number; isPrimaryOwner: boolean; canManageRoles: boolean; + membersBenefitsUsage: { + personal_account_id: string; + benefit_amount: number; + }[]; }; export function AccountMembersTable({ @@ -51,6 +56,7 @@ export function AccountMembersTable({ isPrimaryOwner, userRoleHierarchy, canManageRoles, + membersBenefitsUsage, }: AccountMembersTableProps) { const [search, setSearch] = useState(''); const { t } = useTranslation('teams'); @@ -73,6 +79,7 @@ export function AccountMembersTable({ currentUserId, currentAccountId, currentRoleHierarchy: userRoleHierarchy, + membersBenefitsUsage, }); const filteredMembers = members @@ -122,9 +129,16 @@ function useGetColumns( currentUserId: string; currentAccountId: string; currentRoleHierarchy: number; + membersBenefitsUsage: { + personal_account_id: string; + benefit_amount: number; + }[]; }, ): ColumnDef[] { - const { t } = useTranslation('teams'); + const { + t, + i18n: { language }, + } = useTranslation('teams'); return useMemo( () => [ @@ -168,6 +182,23 @@ function useGetColumns( return row.original.personal_code ?? '-'; }, }, + { + header: t('distributedBenefitsAmount'), + cell: ({ row }) => { + const benefitAmount = params.membersBenefitsUsage.find( + (usage) => usage.personal_account_id === row.original.id, + )?.benefit_amount; + if (typeof benefitAmount !== 'number') { + return '-'; + } + + return formatCurrency({ + currencyCode: 'EUR', + locale: language, + value: benefitAmount, + }); + }, + }, { header: t('roleLabel'), cell: ({ row }) => { @@ -175,7 +206,11 @@ function useGetColumns( const isPrimaryOwner = primary_owner_user_id === user_id; return ( - + diff --git a/packages/features/team-accounts/src/components/settings/team-account-settings-container.tsx b/packages/features/team-accounts/src/components/settings/team-account-settings-container.tsx index c49ae1e..cfa629c 100644 --- a/packages/features/team-accounts/src/components/settings/team-account-settings-container.tsx +++ b/packages/features/team-accounts/src/components/settings/team-account-settings-container.tsx @@ -13,6 +13,8 @@ import { TeamAccountDangerZone } from './team-account-danger-zone'; import { UpdateTeamAccountImage } from './update-team-account-image-container'; import { UpdateTeamAccountNameForm } from './update-team-account-name-form'; +const SHOW_TEAM_LOGO = false as boolean; + export function TeamAccountSettingsContainer(props: { account: { name: string; @@ -32,21 +34,23 @@ export function TeamAccountSettingsContainer(props: { }) { return (
- - - - - + {SHOW_TEAM_LOGO && ( + + + + + - - - - + + + + - - - - + + + + + )} diff --git a/packages/features/team-accounts/src/server/actions/team-invitations-server-actions.ts b/packages/features/team-accounts/src/server/actions/team-invitations-server-actions.ts index 4f11f93..bc438a8 100644 --- a/packages/features/team-accounts/src/server/actions/team-invitations-server-actions.ts +++ b/packages/features/team-accounts/src/server/actions/team-invitations-server-actions.ts @@ -5,6 +5,7 @@ import { redirect } from 'next/navigation'; import { z } from 'zod'; +import { AccountBalanceService } from '@kit/accounts/services/account-balance.service'; import { enhanceAction } from '@kit/next/actions'; import { createNotificationsApi } from '@kit/notifications/api'; import { getLogger } from '@kit/shared/logger'; @@ -148,6 +149,7 @@ export const updateInvitationAction = enhanceAction( export const acceptInvitationAction = enhanceAction( async (data: FormData, user) => { const client = getSupabaseServerClient(); + const accountBalanceService = new AccountBalanceService(); const { inviteToken, nextPath } = AcceptInvitationSchema.parse( Object.fromEntries(data), @@ -171,6 +173,9 @@ export const acceptInvitationAction = enhanceAction( throw new Error('Failed to accept invitation'); } + // Make sure new account gets company benefits added to balance + await accountBalanceService.processPeriodicBenefitDistributions(); + // Increase the seats for the account await perSeatBillingService.increaseSeats(accountId); diff --git a/packages/features/team-accounts/src/server/services/webhooks/account-webhooks.service.ts b/packages/features/team-accounts/src/server/services/webhooks/account-webhooks.service.ts index 9bdc5a3..c7c7fb0 100644 --- a/packages/features/team-accounts/src/server/services/webhooks/account-webhooks.service.ts +++ b/packages/features/team-accounts/src/server/services/webhooks/account-webhooks.service.ts @@ -1,9 +1,7 @@ import { z } from 'zod'; +import type { Account } from '@kit/accounts/types/accounts'; import { getLogger } from '@kit/shared/logger'; -import { Database } from '@kit/supabase/database'; - -type Account = Database['medreport']['Tables']['accounts']['Row']; export function createAccountWebhooksService() { return new AccountWebhooksService(); diff --git a/packages/features/user-analyses/src/server/api.ts b/packages/features/user-analyses/src/server/api.ts index 0532886..93222ce 100644 --- a/packages/features/user-analyses/src/server/api.ts +++ b/packages/features/user-analyses/src/server/api.ts @@ -4,7 +4,10 @@ import type { UuringuVastus } from '@kit/shared/types/medipost-analysis'; import { toArray } from '@kit/shared/utils'; import { Database } from '@kit/supabase/database'; -import type { AnalysisOrder } from '../types/analysis-orders'; +import type { + AnalysisOrder, + AnalysisOrderStatus, +} from '../types/analysis-orders'; import type { AnalysisResultDetailsElement, AnalysisResultDetailsMapped, @@ -450,6 +453,34 @@ class UserAnalysesApi { return data; } + + async updateAnalysisOrderStatus({ + orderId, + medusaOrderId, + orderStatus, + }: { + orderId?: number; + medusaOrderId?: string; + orderStatus: AnalysisOrderStatus; + }) { + const orderIdParam = orderId; + const medusaOrderIdParam = medusaOrderId; + + console.info( + `Updating order id=${orderId} medusaOrderId=${medusaOrderId} status=${orderStatus}`, + ); + if (!orderIdParam && !medusaOrderIdParam) { + throw new Error('Either orderId or medusaOrderId must be provided'); + } + await this.client + .schema('medreport') + .rpc('update_analysis_order_status', { + order_id: orderIdParam ?? -1, + status_param: orderStatus, + medusa_order_id_param: medusaOrderIdParam ?? '', + }) + .throwOnError(); + } } export function createUserAnalysesApi(client: SupabaseClient) { diff --git a/packages/features/user-analyses/src/types/analysis-orders.ts b/packages/features/user-analyses/src/types/analysis-orders.ts index 4ef4027..1eac2f0 100644 --- a/packages/features/user-analyses/src/types/analysis-orders.ts +++ b/packages/features/user-analyses/src/types/analysis-orders.ts @@ -1,3 +1,4 @@ import { Tables } from '@kit/supabase/database'; export type AnalysisOrder = Tables<{ schema: 'medreport' }, 'analysis_orders'>; +export type AnalysisOrderStatus = AnalysisOrder['status']; diff --git a/packages/shared/src/config/team-account-navigation.config.tsx b/packages/shared/src/config/team-account-navigation.config.tsx index ac466cc..45d0867 100644 --- a/packages/shared/src/config/team-account-navigation.config.tsx +++ b/packages/shared/src/config/team-account-navigation.config.tsx @@ -1,9 +1,9 @@ -import { CreditCard, LayoutDashboard, Settings, Users } from 'lucide-react'; +import { Euro, LayoutDashboard, Settings, Users } from 'lucide-react'; import { NavigationConfigSchema } from '@kit/ui/navigation-schema'; -import pathsConfig from './paths.config'; import featureFlagsConfig from './feature-flags.config'; +import pathsConfig from './paths.config'; const iconClasses = 'w-4'; @@ -11,18 +11,13 @@ const getRoutes = (account: string) => [ { children: [ { - label: 'common:routes.dashboard', + label: 'common:routes.companyDashboard', path: pathsConfig.app.accountHome.replace('[account]', account), Icon: , end: true, }, { - label: 'common:routes.settings', - path: createPath(pathsConfig.app.accountSettings, account), - Icon: , - }, - { - label: 'common:routes.members', + label: 'common:routes.companyMembers', path: createPath(pathsConfig.app.accountMembers, account), Icon: , }, @@ -30,9 +25,14 @@ const getRoutes = (account: string) => [ ? { label: 'common:routes.billing', path: createPath(pathsConfig.app.accountBilling, account), - Icon: , + Icon: , } : undefined, + { + label: 'common:routes.companySettings', + path: createPath(pathsConfig.app.accountSettings, account), + Icon: , + }, ].filter(Boolean), }, ]; diff --git a/packages/supabase/src/clients/server-client.ts b/packages/supabase/src/clients/server-client.ts index c9b8d7e..4c20471 100644 --- a/packages/supabase/src/clients/server-client.ts +++ b/packages/supabase/src/clients/server-client.ts @@ -11,10 +11,10 @@ import { getSupabaseClientKeys } from '../get-supabase-client-keys'; * @name getSupabaseServerClient * @description Creates a Supabase client for use in the Server. */ -export function getSupabaseServerClient() { +export function getSupabaseServerClient() { const keys = getSupabaseClientKeys(); - return createServerClient(keys.url, keys.anonKey, { + return createServerClient(keys.url, keys.anonKey, { auth: { flowType: 'pkce', autoRefreshToken: true, diff --git a/packages/supabase/src/database.types.ts b/packages/supabase/src/database.types.ts index d9a0c45..6f03c0b 100644 --- a/packages/supabase/src/database.types.ts +++ b/packages/supabase/src/database.types.ts @@ -348,6 +348,8 @@ export type Database = { expires_at: string | null id: string is_active: boolean + is_analysis_order: boolean + is_analysis_package_order: boolean reference_id: string | null source_company_id: string | null } @@ -361,6 +363,8 @@ export type Database = { expires_at?: string | null id?: string is_active?: boolean + is_analysis_order?: boolean + is_analysis_package_order?: boolean reference_id?: string | null source_company_id?: string | null } @@ -374,6 +378,8 @@ export type Database = { expires_at?: string | null id?: string is_active?: boolean + is_analysis_order?: boolean + is_analysis_package_order?: boolean reference_id?: string | null source_company_id?: string | null } @@ -2310,6 +2316,15 @@ export type Database = { user_id: string }[] } + get_benefits_usages_for_company_members: { + Args: { + p_account_id: string + } + Returns: { + personal_account_id: string + benefit_amount: number + } + } get_config: { Args: Record Returns: Json diff --git a/packages/ui/src/shadcn/skeleton.tsx b/packages/ui/src/shadcn/skeleton.tsx index 5b0ac1e..2f3ba22 100644 --- a/packages/ui/src/shadcn/skeleton.tsx +++ b/packages/ui/src/shadcn/skeleton.tsx @@ -3,21 +3,23 @@ import { cn } from '../lib/utils'; function Skeleton({ className, children, + isLoading = true, ...props -}: React.HTMLAttributes) { +}: React.HTMLAttributes & { isLoading?: boolean }) { return (
-
+
{children ?? }
- -
+ {isLoading && ( +
+ )}
); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1e3acae..3506ae7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -87,10 +87,10 @@ importers: version: 2.10.1(react@19.1.0) '@medusajs/js-sdk': specifier: latest - version: 2.10.2(awilix@8.0.1) + version: 2.10.3(awilix@8.0.1) '@medusajs/ui': specifier: latest - version: 4.0.22(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.2) + version: 4.0.23(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.2) '@nosecone/next': specifier: 1.0.0-beta.7 version: 1.0.0-beta.7(next@15.3.2(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) @@ -196,10 +196,10 @@ importers: version: link:tooling/typescript '@medusajs/types': specifier: latest - version: 2.10.2(awilix@8.0.1) + version: 2.10.3(awilix@8.0.1) '@medusajs/ui-preset': specifier: latest - version: 2.10.2(tailwindcss@4.1.7) + version: 2.10.3(tailwindcss@4.1.7) '@next/bundle-analyzer': specifier: 15.3.2 version: 15.3.2 @@ -560,6 +560,9 @@ importers: '@react-email/components': specifier: 0.0.41 version: 0.0.41(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-email: + specifier: 4.2.12 + version: 4.2.12 devDependencies: '@kit/i18n': specifier: workspace:* @@ -567,6 +570,9 @@ importers: '@kit/tsconfig': specifier: workspace:* version: link:../../tooling/typescript + '@react-email/preview-server': + specifier: 4.2.12 + version: 4.2.12(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.2)(postcss@8.5.6)(ts-node@10.9.2(@types/node@22.18.0)(typescript@5.9.2)) packages/features/accounts: dependencies: @@ -646,6 +652,9 @@ importers: '@hookform/resolvers': specifier: ^5.0.1 version: 5.2.1(react-hook-form@7.62.0(react@19.1.0)) + '@kit/accounts': + specifier: workspace:* + version: link:../accounts '@kit/next': specifier: workspace:* version: link:../../next @@ -754,6 +763,9 @@ importers: '@kit/ui': specifier: workspace:* version: link:../../ui + '@kit/user-analyses': + specifier: workspace:* + version: link:../user-analyses '@makerkit/data-loader-supabase-core': specifier: ^0.0.10 version: 0.0.10(@supabase/postgrest-js@1.19.4)(@supabase/supabase-js@2.49.4) @@ -792,10 +804,10 @@ importers: version: 2.2.7(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106) '@medusajs/js-sdk': specifier: latest - version: 2.10.2(awilix@8.0.1) + version: 2.10.3(awilix@8.0.1) '@medusajs/ui': specifier: latest - version: 4.0.22(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)(typescript@5.9.2) + version: 4.0.23(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)(typescript@5.9.2) '@radix-ui/react-accordion': specifier: ^1.2.1 version: 1.2.12(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106) @@ -841,10 +853,10 @@ importers: version: 7.28.3 '@medusajs/types': specifier: latest - version: 2.10.2(awilix@8.0.1) + version: 2.10.3(awilix@8.0.1) '@medusajs/ui-preset': specifier: latest - version: 2.10.2(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.9.2))) + version: 2.10.3(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.9.2))) '@types/lodash': specifier: ^4.14.195 version: 4.17.20 @@ -1612,6 +1624,13 @@ packages: } engines: { node: '>=6.9.0' } + '@babel/core@7.26.10': + resolution: + { + integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==, + } + engines: { node: '>=6.9.0' } + '@babel/core@7.28.3': resolution: { @@ -1698,6 +1717,14 @@ packages: } engines: { node: '>=6.9.0' } + '@babel/parser@7.27.0': + resolution: + { + integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==, + } + engines: { node: '>=6.0.0' } + hasBin: true + '@babel/parser@7.27.5': resolution: { @@ -1870,6 +1897,13 @@ packages: } engines: { node: '>=6.9.0' } + '@babel/traverse@7.27.0': + resolution: + { + integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==, + } + engines: { node: '>=6.9.0' } + '@babel/traverse@7.27.4': resolution: { @@ -2079,6 +2113,465 @@ packages: integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==, } + '@esbuild/aix-ppc64@0.25.0': + resolution: + { + integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==, + } + engines: { node: '>=18' } + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.25.10': + resolution: + { + integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==, + } + engines: { node: '>=18' } + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.0': + resolution: + { + integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.25.10': + resolution: + { + integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.0': + resolution: + { + integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==, + } + engines: { node: '>=18' } + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.25.10': + resolution: + { + integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==, + } + engines: { node: '>=18' } + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.0': + resolution: + { + integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.25.10': + resolution: + { + integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.0': + resolution: + { + integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.25.10': + resolution: + { + integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.0': + resolution: + { + integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.10': + resolution: + { + integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.0': + resolution: + { + integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.25.10': + resolution: + { + integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.0': + resolution: + { + integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.10': + resolution: + { + integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.0': + resolution: + { + integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.25.10': + resolution: + { + integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.0': + resolution: + { + integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==, + } + engines: { node: '>=18' } + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.25.10': + resolution: + { + integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==, + } + engines: { node: '>=18' } + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.0': + resolution: + { + integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==, + } + engines: { node: '>=18' } + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.25.10': + resolution: + { + integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==, + } + engines: { node: '>=18' } + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.0': + resolution: + { + integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==, + } + engines: { node: '>=18' } + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.25.10': + resolution: + { + integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==, + } + engines: { node: '>=18' } + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.0': + resolution: + { + integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==, + } + engines: { node: '>=18' } + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.25.10': + resolution: + { + integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==, + } + engines: { node: '>=18' } + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.0': + resolution: + { + integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==, + } + engines: { node: '>=18' } + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.25.10': + resolution: + { + integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==, + } + engines: { node: '>=18' } + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.0': + resolution: + { + integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==, + } + engines: { node: '>=18' } + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.10': + resolution: + { + integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==, + } + engines: { node: '>=18' } + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.0': + resolution: + { + integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==, + } + engines: { node: '>=18' } + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.25.10': + resolution: + { + integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==, + } + engines: { node: '>=18' } + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.0': + resolution: + { + integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.25.10': + resolution: + { + integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.0': + resolution: + { + integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-arm64@0.25.10': + resolution: + { + integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.0': + resolution: + { + integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.10': + resolution: + { + integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.0': + resolution: + { + integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-arm64@0.25.10': + resolution: + { + integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.0': + resolution: + { + integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.10': + resolution: + { + integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.10': + resolution: + { + integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.0': + resolution: + { + integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.25.10': + resolution: + { + integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.0': + resolution: + { + integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.25.10': + resolution: + { + integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.0': + resolution: + { + integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==, + } + engines: { node: '>=18' } + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.25.10': + resolution: + { + integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==, + } + engines: { node: '>=18' } + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.0': + resolution: + { + integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.25.10': + resolution: + { + integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.7.0': resolution: { @@ -2338,6 +2831,15 @@ packages: } engines: { node: '>=18.18' } + '@img/sharp-darwin-arm64@0.34.1': + resolution: + { + integrity: sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm64] + os: [darwin] + '@img/sharp-darwin-arm64@0.34.2': resolution: { @@ -2356,6 +2858,15 @@ packages: cpu: [arm64] os: [darwin] + '@img/sharp-darwin-x64@0.34.1': + resolution: + { + integrity: sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [x64] + os: [darwin] + '@img/sharp-darwin-x64@0.34.2': resolution: { @@ -2518,6 +3029,15 @@ packages: cpu: [x64] os: [linux] + '@img/sharp-linux-arm64@0.34.1': + resolution: + { + integrity: sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm64] + os: [linux] + '@img/sharp-linux-arm64@0.34.2': resolution: { @@ -2536,6 +3056,15 @@ packages: cpu: [arm64] os: [linux] + '@img/sharp-linux-arm@0.34.1': + resolution: + { + integrity: sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm] + os: [linux] + '@img/sharp-linux-arm@0.34.2': resolution: { @@ -2563,6 +3092,15 @@ packages: cpu: [ppc64] os: [linux] + '@img/sharp-linux-s390x@0.34.1': + resolution: + { + integrity: sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [s390x] + os: [linux] + '@img/sharp-linux-s390x@0.34.2': resolution: { @@ -2581,6 +3119,15 @@ packages: cpu: [s390x] os: [linux] + '@img/sharp-linux-x64@0.34.1': + resolution: + { + integrity: sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [x64] + os: [linux] + '@img/sharp-linux-x64@0.34.2': resolution: { @@ -2599,6 +3146,15 @@ packages: cpu: [x64] os: [linux] + '@img/sharp-linuxmusl-arm64@0.34.1': + resolution: + { + integrity: sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm64] + os: [linux] + '@img/sharp-linuxmusl-arm64@0.34.2': resolution: { @@ -2617,6 +3173,15 @@ packages: cpu: [arm64] os: [linux] + '@img/sharp-linuxmusl-x64@0.34.1': + resolution: + { + integrity: sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [x64] + os: [linux] + '@img/sharp-linuxmusl-x64@0.34.2': resolution: { @@ -2635,6 +3200,14 @@ packages: cpu: [x64] os: [linux] + '@img/sharp-wasm32@0.34.1': + resolution: + { + integrity: sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [wasm32] + '@img/sharp-wasm32@0.34.2': resolution: { @@ -2669,6 +3242,15 @@ packages: cpu: [arm64] os: [win32] + '@img/sharp-win32-ia32@0.34.1': + resolution: + { + integrity: sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [ia32] + os: [win32] + '@img/sharp-win32-ia32@0.34.2': resolution: { @@ -2687,6 +3269,15 @@ packages: cpu: [ia32] os: [win32] + '@img/sharp-win32-x64@0.34.1': + resolution: + { + integrity: sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [x64] + os: [win32] + '@img/sharp-win32-x64@0.34.2': resolution: { @@ -2729,6 +3320,20 @@ packages: integrity: sha512-D4OHBjrinH+PFZPvfCXvG28n2LSykWcJ7GIioQL+ok0LON15SdfoUssoHzzOUmVZLbRoREsQXVzA6r8JKsbP6A==, } + '@isaacs/balanced-match@4.0.1': + resolution: + { + integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==, + } + engines: { node: 20 || >=22 } + + '@isaacs/brace-expansion@5.0.0': + resolution: + { + integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==, + } + engines: { node: 20 || >=22 } + '@isaacs/cliui@8.0.2': resolution: { @@ -3122,6 +3727,20 @@ packages: } engines: { node: '>=20' } + '@lottiefiles/dotlottie-react@0.13.3': + resolution: + { + integrity: sha512-V4FfdYlqzjBUX7f0KV6vfQOOI0Cp+3XeG/ZqSDFSEVg5P7fpROpDv5/I9aTM8sOCESK1SWT96Fem+QVUnBV1wQ==, + } + peerDependencies: + react: ^17 || ^18 || ^19 + + '@lottiefiles/dotlottie-web@0.42.0': + resolution: + { + integrity: sha512-Zr2LCaOAoPCsdAQgeLyCSiQ1+xrAJtRCyuEYDj0qR5heUwpc+Pxbb88JyTVumcXFfKOBMOMmrlsTScLz2mrvQQ==, + } + '@makerkit/data-loader-supabase-core@0.0.10': resolution: { @@ -3189,25 +3808,25 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - '@medusajs/icons@2.10.2': + '@medusajs/icons@2.10.3': resolution: { - integrity: sha512-ZZFEWTGdQGvsRPs5ANV9GlFUFyba852cqWtRu7aO3QlPhk+ECbWqJYkhL3h/HZ1twN5nEG51QAncUkjm5TVBRw==, + integrity: sha512-w1NQzNY/cTsfjuEQ8AOpEI49mxA6Oa6tJlg4HMfpOjDl3/R2qZtQXa8DIchSr3Sx+/RTsIGYUxv4Vbd+VTszJg==, } peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - '@medusajs/js-sdk@2.10.2': + '@medusajs/js-sdk@2.10.3': resolution: { - integrity: sha512-P6A9E5LQkG/g9YiHlvUbH/k777XTfnY47neI1EmA56B66XBVIwzTqUglluREVZ+akjlI0eOxpwk2PDFPVaK1ZQ==, + integrity: sha512-S2B6ViUFrZeCQnshJCjuhzLJmcb3eIVvdl/W4u/TcQGSF+cMXQGEsB9T7QUC6pyloY2+g5tTYxO+ND8s0enTCg==, } engines: { node: '>=20' } - '@medusajs/types@2.10.2': + '@medusajs/types@2.10.3': resolution: { - integrity: sha512-yonCLLnO2FFDB+HCo6nAGz+I7u+7ux+HDJCT8KUPlZJ5/6AYZcgdsuNWMwaa441yjEXDDdZmGZtbSM37lBFoxA==, + integrity: sha512-Rx1H4LZ17AsOspaFAPpMwpi2rxj14ZWSip+1FA28GZourviWCLR2i3Er0b6SqJdh861fH6RhSyNaYVEKX6rk/w==, } engines: { node: '>=20' } peerDependencies: @@ -3220,18 +3839,18 @@ packages: vite: optional: true - '@medusajs/ui-preset@2.10.2': + '@medusajs/ui-preset@2.10.3': resolution: { - integrity: sha512-t3VmloSbeZOjfyYL+iQTU94wutX0/B+uv9wz8L1MK6CErHK4T4ox246QDsNAR2QrNE0EUEEy+SX0qiW+xHFsMQ==, + integrity: sha512-rpWJtg4pLOxKYWA9sqhWkOgG2B0npyGO4nMqqAOh4Xl/LNCkeyeQ3Hmwj0v2mH8jhum65E/mxDVpjQ/lWFVpqQ==, } peerDependencies: tailwindcss: '>=3.0.0' - '@medusajs/ui@4.0.22': + '@medusajs/ui@4.0.23': resolution: { - integrity: sha512-p3Nl6OTyxe583VVmvGAYbizFFPzr9GsZzbO54Ku9jZqf+MYNqdDjKFP28K5QHkpLpbvkGU05BQZ5d5H49vGxiQ==, + integrity: sha512-jXQEIv6nluO+P5KxPrS9QYfQ2U/HO3Vl2cmZZVMdaNB1ol3y7pcQq5FEs00IgyjQv4DsK209bj8cicqnvVvRWA==, } peerDependencies: react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc @@ -4060,6 +4679,12 @@ packages: integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==, } + '@radix-ui/colors@3.0.0': + resolution: + { + integrity: sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==, + } + '@radix-ui/number@1.1.0': resolution: { @@ -4202,6 +4827,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-arrow@1.1.4': + resolution: + { + integrity: sha512-qz+fxrqgNxG0dYew5l7qR3c7wdgRu1XVUHGnGYX7rg5HM4p9SWaRmJwfgR3J0SgyUKayLmzQIun+N6rWRgiRKw==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-arrow@1.1.6': resolution: { @@ -4362,6 +5003,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-collapsible@1.1.7': + resolution: + { + integrity: sha512-zGFsPcFJNdQa/UNd6MOgF40BS054FIGj32oOWBllixz42f+AkQg3QJ1YT9pw7vs+Ai+EgWkh839h69GEK8oH2A==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-collection@1.1.1': resolution: { @@ -4378,6 +5035,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-collection@1.1.4': + resolution: + { + integrity: sha512-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-collection@1.1.6': resolution: { @@ -4594,6 +5267,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-dismissable-layer@1.1.7': + resolution: + { + integrity: sha512-j5+WBUdhccJsmH5/H0K6RncjDtoALSEr6jbkaZu+bjw6hOPOhHycr6vEUujl+HBK8kjUfWcoCJXxP6e4lUlMZw==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-dismissable-layer@1.1.9': resolution: { @@ -4610,6 +5299,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-dropdown-menu@2.1.10': + resolution: + { + integrity: sha512-8qnILty92BmXbxKugWX3jgEeFeMoxtdggeCCxb/aB7l34QFAKB23IhJfnwyVMbRnAUJiT5LOay4kUS22+AWuRg==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-dropdown-menu@2.1.16': resolution: { @@ -4654,6 +5359,18 @@ packages: '@types/react': optional: true + '@radix-ui/react-focus-guards@1.1.2': + resolution: + { + integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==, + } + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-focus-guards@1.1.3': resolution: { @@ -4682,6 +5399,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-focus-scope@1.1.4': + resolution: + { + integrity: sha512-r2annK27lIW5w9Ho5NyQgqs0MmgZSTIKXWpVCJaLC1q2kZrZkcqnmHkCHMEmv8XLvsLlurKMPT+kbKkRkm/xVA==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-focus-scope@1.1.7': resolution: { @@ -4794,6 +5527,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-menu@2.1.10': + resolution: + { + integrity: sha512-OupA+1PrVf2H0K4jIwkDyA+rsJ7vF1y/VxLEO43dmZ68GtCjvx9K1/B/QscPZM3jIeFNK/wPd0HmiLjT36hVcA==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-menu@2.1.16': resolution: { @@ -4874,6 +5623,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-popover@1.1.10': + resolution: + { + integrity: sha512-IZN7b3sXqajiPsOzKuNJBSP9obF4MX5/5UhTgWNofw4r1H+eATWb0SyMlaxPD/kzA4vadFgy1s7Z1AEJ6WMyHQ==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-popover@1.1.15': resolution: { @@ -4922,6 +5687,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-popper@1.2.4': + resolution: + { + integrity: sha512-3p2Rgm/a1cK0r/UVkx5F/K9v/EplfjAeIFCGOPYPO4lZ0jtg4iSQXt/YGTSLWaf4x7NG6Z4+uKFcylcTZjeqDA==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-popper@1.2.6': resolution: { @@ -4970,6 +5751,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-portal@1.1.6': + resolution: + { + integrity: sha512-XmsIl2z1n/TsYFLIdYam2rmFwf9OC/Sh2avkbmVMDuBZIe7hSpM0cYnWPAo7nHOVx8zTuwDZGByfcqLdnzp3Vw==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-portal@1.1.8': resolution: { @@ -5018,6 +5815,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-presence@1.1.3': + resolution: + { + integrity: sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-presence@1.1.4': resolution: { @@ -5066,6 +5879,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-primitive@2.1.0': + resolution: + { + integrity: sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-primitive@2.1.2': resolution: { @@ -5194,6 +6023,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-roving-focus@1.1.6': + resolution: + { + integrity: sha512-D2ReXCuIueKf5L2f1ks/wTj3bWck1SvK1pjLmEHPbwksS1nOHBsvgY0b9Hypt81FczqBqSyLHQxn/vbsQ0gDHw==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-scroll-area@1.2.10': resolution: { @@ -5318,6 +6163,18 @@ packages: '@types/react': optional: true + '@radix-ui/react-slot@1.2.0': + resolution: + { + integrity: sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==, + } + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-slot@1.2.2': resolution: { @@ -5406,6 +6263,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-tabs@1.1.7': + resolution: + { + integrity: sha512-sawt4HkD+6haVGjYOC3BMIiCumBpqTK6o407n6zN/6yReed2EN7bXyykNrpqg+xCfudpBUZg7Y2cJBd/x/iybA==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-toast@1.2.15': resolution: { @@ -5454,6 +6327,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-toggle-group@1.1.6': + resolution: + { + integrity: sha512-XOBq9VqC+mIn5hzjGdJLhQbvQeiOpV5ExNE6qMQQPvFsCT44QUcxFzYytTWVoyWg9XKfgrleKmTeEyu6aoTPhg==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-toggle@1.1.1': resolution: { @@ -5470,6 +6359,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-toggle@1.1.6': + resolution: + { + integrity: sha512-3SeJxKeO3TO1zVw1Nl++Cp0krYk6zHDHMCUXXVkosIzl6Nxcvb07EerQpyD2wXQSJ5RZajrYAmPaydU8Hk1IyQ==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-toolbar@1.1.1': resolution: { @@ -5502,6 +6407,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-tooltip@1.2.3': + resolution: + { + integrity: sha512-0KX7jUYFA02np01Y11NWkk6Ip6TqMNmD4ijLelYAzeIndl2aVeltjJFJ2gwjNa1P8U/dgjQ+8cr9Y3Ni+ZNoRA==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-tooltip@1.2.6': resolution: { @@ -5726,6 +6647,22 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-visually-hidden@1.2.0': + resolution: + { + integrity: sha512-rQj0aAWOpCdCMRbI6pLQm8r7S2BM3YhTa0SzOYD55k+hJA8oo9J+H+9wLM9oMlZWOX/wJWPTzfDfmZkf7LvCfg==, + } + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-visually-hidden@1.2.2': resolution: { @@ -6620,6 +7557,12 @@ packages: peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc + '@react-email/preview-server@4.2.12': + resolution: + { + integrity: sha512-FVM3h6vJQdjk5E3P8ts8zAPeZSyefcIDWGmy/Tnwl9zVlwiGcQSjGVPmAHlw1DdJnkK79OeC/VYisMlq0w6W8Q==, + } + '@react-email/preview@0.0.13': resolution: { @@ -7580,6 +8523,12 @@ packages: integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==, } + '@socket.io/component-emitter@3.1.2': + resolution: + { + integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==, + } + '@standard-schema/utils@0.3.0': resolution: { @@ -8001,6 +8950,12 @@ packages: integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==, } + '@types/cors@2.8.19': + resolution: + { + integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==, + } + '@types/d3-array@3.2.1': resolution: { @@ -8199,6 +9154,12 @@ packages: integrity: sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==, } + '@types/node@22.14.1': + resolution: + { + integrity: sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==, + } + '@types/node@22.18.0': resolution: { @@ -8217,6 +9178,12 @@ packages: integrity: sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==, } + '@types/normalize-path@3.0.2': + resolution: + { + integrity: sha512-DO++toKYPaFn0Z8hQ7Tx+3iT9t77IJo/nDiqTXilgEP+kPNIYdpS9kh3fXuc53ugqwp9pxC1PVjCpV1tQDyqMA==, + } + '@types/parse-json@4.0.2': resolution: { @@ -8267,6 +9234,14 @@ packages: peerDependencies: '@types/react': ^18.0.0 + '@types/react-dom@19.0.4': + resolution: + { + integrity: sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==, + } + peerDependencies: + '@types/react': ^19.0.0 + '@types/react-dom@19.1.5': resolution: { @@ -8293,6 +9268,12 @@ packages: integrity: sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==, } + '@types/react@19.0.10': + resolution: + { + integrity: sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==, + } + '@types/react@19.1.4': resolution: { @@ -8329,6 +9310,12 @@ packages: integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==, } + '@types/webpack@5.28.5': + resolution: + { + integrity: sha512-wR87cgvxj3p6D0Crt1r5avwqffqPXUkNlnQ1mjU93G7gCuFjufZR4I6j8cz5g1F1tTYpfOOFvly+cmIQwL9wvw==, + } + '@types/ws@8.18.1': resolution: { @@ -8781,6 +9768,13 @@ packages: integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==, } + accepts@1.3.8: + resolution: + { + integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==, + } + engines: { node: '>= 0.6' } + acorn-import-assertions@1.9.0: resolution: { @@ -9233,6 +10227,19 @@ packages: integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, } + base64-js@1.5.1: + resolution: + { + integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==, + } + + base64id@2.0.0: + resolution: + { + integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==, + } + engines: { node: ^4.5.0 || >= 5.9 } + big.js@5.2.2: resolution: { @@ -9259,6 +10266,12 @@ packages: } engines: { node: '>=8' } + bl@4.1.0: + resolution: + { + integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==, + } + boolbase@1.0.0: resolution: { @@ -9325,6 +10338,12 @@ packages: integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==, } + buffer@5.7.1: + resolution: + { + integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==, + } + busboy@1.6.0: resolution: { @@ -9425,6 +10444,13 @@ packages: } engines: { node: '>=10' } + chalk@5.6.2: + resolution: + { + integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==, + } + engines: { node: ^12.17.0 || ^14.13 || >=16.0.0 } + char-regex@1.0.2: resolution: { @@ -9463,6 +10489,13 @@ packages: } engines: { node: '>= 8.10.0' } + chokidar@4.0.3: + resolution: + { + integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==, + } + engines: { node: '>= 14.16.0' } + chownr@3.0.0: resolution: { @@ -9491,6 +10524,12 @@ packages: } engines: { node: '>=8' } + citty@0.1.6: + resolution: + { + integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==, + } + cjs-module-lexer@1.4.3: resolution: { @@ -9509,6 +10548,27 @@ packages: integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==, } + cli-cursor@3.1.0: + resolution: + { + integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==, + } + engines: { node: '>=8' } + + cli-cursor@5.0.0: + resolution: + { + integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==, + } + engines: { node: '>=18' } + + cli-spinners@2.9.2: + resolution: + { + integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==, + } + engines: { node: '>=6' } + client-only@0.0.1: resolution: { @@ -9522,6 +10582,13 @@ packages: } engines: { node: '>=12' } + clone@1.0.4: + resolution: + { + integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==, + } + engines: { node: '>=0.8' } + clsx@1.2.1: resolution: { @@ -9624,6 +10691,13 @@ packages: } engines: { node: '>=16' } + commander@13.1.0: + resolution: + { + integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==, + } + engines: { node: '>=18' } + commander@2.20.3: resolution: { @@ -9668,6 +10742,19 @@ packages: integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, } + confbox@0.2.2: + resolution: + { + integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==, + } + + consola@3.4.2: + resolution: + { + integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==, + } + engines: { node: ^14.18.0 || >=16.10.0 } + convert-source-map@1.9.0: resolution: { @@ -9680,6 +10767,13 @@ packages: integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, } + cookie@0.7.2: + resolution: + { + integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==, + } + engines: { node: '>= 0.6' } + cookie@1.0.2: resolution: { @@ -9693,6 +10787,13 @@ packages: integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==, } + cors@2.8.5: + resolution: + { + integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==, + } + engines: { node: '>= 0.10' } + cosmiconfig@7.1.0: resolution: { @@ -9945,6 +11046,13 @@ packages: integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==, } + debounce@2.2.0: + resolution: + { + integrity: sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==, + } + engines: { node: '>=18' } + debug@3.2.7: resolution: { @@ -9956,6 +11064,18 @@ packages: supports-color: optional: true + debug@4.3.7: + resolution: + { + integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==, + } + engines: { node: '>=6.0' } + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.4.1: resolution: { @@ -10010,6 +11130,12 @@ packages: } engines: { node: '>=0.10.0' } + defaults@1.0.4: + resolution: + { + integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==, + } + define-data-property@1.1.4: resolution: { @@ -10199,6 +11325,12 @@ packages: } engines: { node: '>=12' } + emoji-regex@10.5.0: + resolution: + { + integrity: sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==, + } + emoji-regex@8.0.0: resolution: { @@ -10224,6 +11356,26 @@ packages: integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==, } + engine.io-client@6.6.3: + resolution: + { + integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==, + } + + engine.io-parser@5.2.3: + resolution: + { + integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==, + } + engines: { node: '>=10.0.0' } + + engine.io@6.6.4: + resolution: + { + integrity: sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==, + } + engines: { node: '>=10.2.0' } + enhanced-resolve@5.18.1: resolution: { @@ -10313,6 +11465,22 @@ packages: } engines: { node: '>= 0.4' } + esbuild@0.25.0: + resolution: + { + integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==, + } + engines: { node: '>=18' } + hasBin: true + + esbuild@0.25.10: + resolution: + { + integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==, + } + engines: { node: '>=18' } + hasBin: true + escalade@3.2.0: resolution: { @@ -10634,6 +11802,12 @@ packages: } engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + exsolve@1.0.7: + resolution: + { + integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==, + } + facepaint@1.2.1: resolution: { @@ -10867,6 +12041,23 @@ packages: integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==, } + framer-motion@12.23.12: + resolution: + { + integrity: sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==, + } + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + fs.realpath@1.0.0: resolution: { @@ -10914,6 +12105,13 @@ packages: } engines: { node: 6.* || 8.* || >= 10.* } + get-east-asian-width@1.4.0: + resolution: + { + integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==, + } + engines: { node: '>=18' } + get-intrinsic@1.3.0: resolution: { @@ -10989,6 +12187,14 @@ packages: } hasBin: true + glob@11.0.3: + resolution: + { + integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==, + } + engines: { node: 20 || >=22 } + hasBin: true + glob@7.2.3: resolution: { @@ -11129,6 +12335,13 @@ packages: } engines: { node: '>= 0.4' } + he@1.2.0: + resolution: + { + integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==, + } + hasBin: true + help-me@5.0.0: resolution: { @@ -11222,6 +12435,12 @@ packages: integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==, } + ieee754@1.2.1: + resolution: + { + integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==, + } + ignore@5.3.2: resolution: { @@ -11483,6 +12702,20 @@ packages: integrity: sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==, } + is-interactive@1.0.0: + resolution: + { + integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==, + } + engines: { node: '>=8' } + + is-interactive@2.0.0: + resolution: + { + integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==, + } + engines: { node: '>=12' } + is-map@2.0.3: resolution: { @@ -11573,6 +12806,27 @@ packages: } engines: { node: '>= 0.4' } + is-unicode-supported@0.1.0: + resolution: + { + integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==, + } + engines: { node: '>=10' } + + is-unicode-supported@1.3.0: + resolution: + { + integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==, + } + engines: { node: '>=12' } + + is-unicode-supported@2.1.0: + resolution: + { + integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==, + } + engines: { node: '>=18' } + is-weakmap@2.0.2: resolution: { @@ -11680,6 +12934,13 @@ packages: integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==, } + jackspeak@4.1.1: + resolution: + { + integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==, + } + engines: { node: 20 || >=22 } + javascript-natural-sort@0.7.1: resolution: { @@ -12098,6 +13359,13 @@ packages: } hasBin: true + jiti@2.4.2: + resolution: + { + integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==, + } + hasBin: true + jiti@2.5.1: resolution: { @@ -12375,6 +13643,13 @@ packages: } engines: { node: '>= 12.0.0' } + lilconfig@2.1.0: + resolution: + { + integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==, + } + engines: { node: '>=10' } + lilconfig@3.1.3: resolution: { @@ -12494,6 +13769,27 @@ packages: integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==, } + log-symbols@4.1.0: + resolution: + { + integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==, + } + engines: { node: '>=10' } + + log-symbols@6.0.0: + resolution: + { + integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==, + } + engines: { node: '>=18' } + + log-symbols@7.0.1: + resolution: + { + integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==, + } + engines: { node: '>=18' } + long@5.3.2: resolution: { @@ -12525,6 +13821,13 @@ packages: integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==, } + lru-cache@11.2.2: + resolution: + { + integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==, + } + engines: { node: 20 || >=22 } + lru-cache@5.1.1: resolution: { @@ -12910,6 +14213,13 @@ packages: } engines: { node: '>= 0.6' } + mime-db@1.54.0: + resolution: + { + integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==, + } + engines: { node: '>= 0.6' } + mime-types@2.1.35: resolution: { @@ -12917,6 +14227,13 @@ packages: } engines: { node: '>= 0.6' } + mime-types@3.0.1: + resolution: + { + integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==, + } + engines: { node: '>= 0.6' } + mimic-fn@2.1.0: resolution: { @@ -12924,6 +14241,13 @@ packages: } engines: { node: '>=6' } + mimic-function@5.0.1: + resolution: + { + integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==, + } + engines: { node: '>=18' } + mini-svg-data-uri@1.4.4: resolution: { @@ -12931,6 +14255,13 @@ packages: } hasBin: true + minimatch@10.0.3: + resolution: + { + integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==, + } + engines: { node: 20 || >=22 } + minimatch@3.1.2: resolution: { @@ -12992,6 +14323,18 @@ packages: integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==, } + motion-dom@12.23.21: + resolution: + { + integrity: sha512-5xDXx/AbhrfgsQmSE7YESMn4Dpo6x5/DTZ4Iyy4xqDvVHWvFVoV+V2Ri2S/ksx+D40wrZ7gPYiMWshkdoqNgNQ==, + } + + motion-utils@12.23.6: + resolution: + { + integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==, + } + mrmime@2.0.1: resolution: { @@ -13041,6 +14384,13 @@ packages: integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, } + negotiator@0.6.3: + resolution: + { + integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==, + } + engines: { node: '>= 0.6' } + neo-async@2.6.2: resolution: { @@ -13147,6 +14497,12 @@ packages: } engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + node-html-parser@7.0.1: + resolution: + { + integrity: sha512-KGtmPY2kS0thCWGK0VuPyOS+pBKhhe8gXztzA2ilAOhbUbxa9homF1bOyKvhGzMLXUoRds9IOmr/v5lr/lqNmA==, + } + node-int64@0.4.0: resolution: { @@ -13207,6 +14563,14 @@ packages: integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==, } + nypm@0.6.0: + resolution: + { + integrity: sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==, + } + engines: { node: ^14.16.0 || >=16.10.0 } + hasBin: true + object-assign@4.1.1: resolution: { @@ -13290,6 +14654,13 @@ packages: } engines: { node: '>=6' } + onetime@7.0.0: + resolution: + { + integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==, + } + engines: { node: '>=18' } + openai@5.20.3: resolution: { @@ -13319,6 +14690,20 @@ packages: } engines: { node: '>= 0.8.0' } + ora@5.4.1: + resolution: + { + integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==, + } + engines: { node: '>=10' } + + ora@8.2.0: + resolution: + { + integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==, + } + engines: { node: '>=18' } + orderedmap@2.1.1: resolution: { @@ -13445,6 +14830,13 @@ packages: } engines: { node: '>=16 || 14 >=14.18' } + path-scurry@2.0.0: + resolution: + { + integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==, + } + engines: { node: 20 || >=22 } + path-type@4.0.0: resolution: { @@ -13452,6 +14844,12 @@ packages: } engines: { node: '>=8' } + pathe@2.0.3: + resolution: + { + integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, + } + peberminta@0.9.0: resolution: { @@ -13589,6 +14987,12 @@ packages: } engines: { node: '>=8' } + pkg-types@2.3.0: + resolution: + { + integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==, + } + possible-typed-array-names@1.1.0: resolution: { @@ -14022,6 +15426,13 @@ packages: engines: { node: '>=14' } hasBin: true + pretty-bytes@6.1.1: + resolution: + { + integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==, + } + engines: { node: ^14.13.1 || >=16.0.0 } + pretty-format@29.7.0: resolution: { @@ -14252,6 +15663,14 @@ packages: date-fns: ^2.28.0 || ^3.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom@19.0.0: + resolution: + { + integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==, + } + peerDependencies: + react: ^19.0.0 + react-dom@19.0.0-rc-66855b96-20241106: resolution: { @@ -14268,6 +15687,14 @@ packages: peerDependencies: react: ^19.1.0 + react-email@4.2.12: + resolution: + { + integrity: sha512-Cp6nYAG9uL4//X/96wDSKxGqVTXsaRMuD1ub0G0iBcfTqLel4VVre3kicq7RraplDuj+ATlqSZu47d7P2ULP5w==, + } + engines: { node: '>=18.0.0' } + hasBin: true + react-error-boundary@4.1.2: resolution: { @@ -14396,6 +15823,13 @@ packages: react: '>=16.6.0' react-dom: '>=16.6.0' + react@19.0.0: + resolution: + { + integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==, + } + engines: { node: '>=0.10.0' } + react@19.0.0-rc-66855b96-20241106: resolution: { @@ -14423,6 +15857,13 @@ packages: } engines: { node: ^18.17.0 || >=20.5.0 } + readable-stream@3.6.2: + resolution: + { + integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==, + } + engines: { node: '>= 6' } + readdirp@3.6.0: resolution: { @@ -14430,6 +15871,13 @@ packages: } engines: { node: '>=8.10.0' } + readdirp@4.1.2: + resolution: + { + integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==, + } + engines: { node: '>= 14.18.0' } + real-require@0.2.0: resolution: { @@ -14550,6 +15998,20 @@ packages: } hasBin: true + restore-cursor@3.1.0: + resolution: + { + integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==, + } + engines: { node: '>=8' } + + restore-cursor@5.1.0: + resolution: + { + integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==, + } + engines: { node: '>=18' } + reusify@1.1.0: resolution: { @@ -14617,6 +16079,12 @@ packages: integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==, } + scheduler@0.25.0: + resolution: + { + integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==, + } + scheduler@0.25.0-rc-66855b96-20241106: resolution: { @@ -14715,6 +16183,13 @@ packages: } engines: { node: '>= 0.4' } + sharp@0.34.1: + resolution: + { + integrity: sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + sharp@0.34.2: resolution: { @@ -14840,6 +16315,33 @@ packages: integrity: sha512-aUJ3rpjrdi5SbJ5G1Qjr3arytfRkEStTmHjBfWq2A2Q8MybacIzkScSvGJjQkdTk3djCK9C9SEOt39sSeZFwTw==, } + socket.io-adapter@2.5.5: + resolution: + { + integrity: sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==, + } + + socket.io-client@4.8.1: + resolution: + { + integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==, + } + engines: { node: '>=10.0.0' } + + socket.io-parser@4.2.4: + resolution: + { + integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==, + } + engines: { node: '>=10.0.0' } + + socket.io@4.8.1: + resolution: + { + integrity: sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==, + } + engines: { node: '>=10.2.0' } + sonic-boom@4.2.0: resolution: { @@ -14855,6 +16357,15 @@ packages: react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + sonner@2.0.3: + resolution: + { + integrity: sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==, + } + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + sonner@2.0.7: resolution: { @@ -14897,6 +16408,12 @@ packages: } engines: { node: '>=0.10.0' } + spamc@0.0.5: + resolution: + { + integrity: sha512-jYXItuZuiWZyG9fIdvgTUbp2MNRuyhuSwvvhhpPJd4JK/9oSZxkD7zAj53GJtowSlXwCJzLg6sCKAoE9wXsKgg==, + } + split2@4.2.0: resolution: { @@ -14930,6 +16447,13 @@ packages: } engines: { node: '>=6' } + stdin-discarder@0.2.2: + resolution: + { + integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==, + } + engines: { node: '>=18' } + stop-iteration-iterator@1.1.0: resolution: { @@ -14965,6 +16489,13 @@ packages: } engines: { node: '>=12' } + string-width@7.2.0: + resolution: + { + integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==, + } + engines: { node: '>=18' } + string.prototype.includes@2.0.1: resolution: { @@ -15006,6 +16537,12 @@ packages: } engines: { node: '>= 0.4' } + string_decoder@1.3.0: + resolution: + { + integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==, + } + stringify-entities@4.0.4: resolution: { @@ -15174,6 +16711,12 @@ packages: integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==, } + tailwind-merge@3.2.0: + resolution: + { + integrity: sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==, + } + tailwind-merge@3.3.1: resolution: { @@ -15194,6 +16737,14 @@ packages: integrity: sha512-N49SnciSeRgLC+VK+Fu5VULNJIvJUvN7tUKF1kEHPXrS76WAlwrSVthCbJ9NUw0Cj/ptxs73pdVEdosomAN5Lg==, } + tailwindcss@3.4.0: + resolution: + { + integrity: sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==, + } + engines: { node: '>=14.0.0' } + hasBin: true + tailwindcss@3.4.17: resolution: { @@ -15312,6 +16863,12 @@ packages: integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==, } + tinyexec@0.3.2: + resolution: + { + integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==, + } + tinyglobby@0.2.14: resolution: { @@ -15431,6 +16988,13 @@ packages: integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==, } + tsconfig-paths@4.2.0: + resolution: + { + integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==, + } + engines: { node: '>=6' } + tslib@2.8.1: resolution: { @@ -15693,6 +17257,15 @@ packages: '@types/react': optional: true + use-debounce@10.0.4: + resolution: + { + integrity: sha512-6Cf7Yr7Wk7Kdv77nnJMf6de4HuDE4dTxKij+RqE9rufDsI6zsbjyAxcH5y2ueJCQAnfgKbzXbZHYlkFwmBlWkw==, + } + engines: { node: '>= 16.0.0' } + peerDependencies: + react: '*' + use-sidecar@1.1.3: resolution: { @@ -15740,6 +17313,13 @@ packages: } engines: { node: '>=10.12.0' } + vary@1.1.2: + resolution: + { + integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==, + } + engines: { node: '>= 0.8' } + vfile-message@4.0.2: resolution: { @@ -15778,6 +17358,12 @@ packages: } engines: { node: '>=10.13.0' } + wcwidth@1.0.1: + resolution: + { + integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==, + } + web-streams-polyfill@3.3.3: resolution: { @@ -15954,6 +17540,21 @@ packages: utf-8-validate: optional: true + ws@8.17.1: + resolution: + { + integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==, + } + engines: { node: '>=10.0.0' } + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.18.2: resolution: { @@ -15969,6 +17570,13 @@ packages: utf-8-validate: optional: true + xmlhttprequest-ssl@2.1.2: + resolution: + { + integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==, + } + engines: { node: '>=0.4.0' } + xtend@4.0.2: resolution: { @@ -16076,12 +17684,25 @@ packages: } engines: { node: '>=10' } + yoctocolors@2.1.2: + resolution: + { + integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==, + } + engines: { node: '>=18' } + yup@1.7.0: resolution: { integrity: sha512-VJce62dBd+JQvoc+fCVq+KZfPHr+hXaxCcVgotfwWvlR0Ja3ffYKaJBT8rptPOSKOGJDCUnW2C2JWpud7aRP6Q==, } + zod@3.24.3: + resolution: + { + integrity: sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==, + } + zod@4.1.5: resolution: { @@ -16193,6 +17814,26 @@ snapshots: '@babel/compat-data@7.27.5': {} + '@babel/core@7.26.10': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.26.10) + '@babel/helpers': 7.28.3 + '@babel/parser': 7.28.3 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.3 + '@babel/types': 7.28.2 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/core@7.28.3': dependencies: '@ampproject/remapping': 2.3.0 @@ -16246,6 +17887,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.28.3(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.3 + transitivePeerDependencies: + - supports-color + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.3)': dependencies: '@babel/core': 7.28.3 @@ -16268,6 +17918,10 @@ snapshots: '@babel/template': 7.27.2 '@babel/types': 7.28.2 + '@babel/parser@7.27.0': + dependencies: + '@babel/types': 7.28.2 + '@babel/parser@7.27.5': dependencies: '@babel/types': 7.27.6 @@ -16369,6 +18023,18 @@ snapshots: '@babel/parser': 7.28.3 '@babel/types': 7.28.2 + '@babel/traverse@7.27.0': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/parser': 7.28.3 + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + debug: 4.4.1 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + '@babel/traverse@7.27.4': dependencies: '@babel/code-frame': 7.27.1 @@ -16467,9 +18133,9 @@ snapshots: react-dom: 19.1.0(react@19.1.0) tslib: 2.8.1 - '@dnd-kit/sortable@7.0.2(@dnd-kit/core@6.3.1(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)': + '@dnd-kit/sortable@7.0.2(@dnd-kit/core@6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.0.0-rc-66855b96-20241106)': dependencies: - '@dnd-kit/core': 6.3.1(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106) + '@dnd-kit/core': 6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@dnd-kit/utilities': 3.2.2(react@19.0.0-rc-66855b96-20241106) react: 19.0.0-rc-66855b96-20241106 tslib: 2.8.1 @@ -16572,6 +18238,159 @@ snapshots: '@emotion/weak-memoize@0.4.0': {} + '@esbuild/aix-ppc64@0.25.0': + optional: true + + '@esbuild/aix-ppc64@0.25.10': + optional: true + + '@esbuild/android-arm64@0.25.0': + optional: true + + '@esbuild/android-arm64@0.25.10': + optional: true + + '@esbuild/android-arm@0.25.0': + optional: true + + '@esbuild/android-arm@0.25.10': + optional: true + + '@esbuild/android-x64@0.25.0': + optional: true + + '@esbuild/android-x64@0.25.10': + optional: true + + '@esbuild/darwin-arm64@0.25.0': + optional: true + + '@esbuild/darwin-arm64@0.25.10': + optional: true + + '@esbuild/darwin-x64@0.25.0': + optional: true + + '@esbuild/darwin-x64@0.25.10': + optional: true + + '@esbuild/freebsd-arm64@0.25.0': + optional: true + + '@esbuild/freebsd-arm64@0.25.10': + optional: true + + '@esbuild/freebsd-x64@0.25.0': + optional: true + + '@esbuild/freebsd-x64@0.25.10': + optional: true + + '@esbuild/linux-arm64@0.25.0': + optional: true + + '@esbuild/linux-arm64@0.25.10': + optional: true + + '@esbuild/linux-arm@0.25.0': + optional: true + + '@esbuild/linux-arm@0.25.10': + optional: true + + '@esbuild/linux-ia32@0.25.0': + optional: true + + '@esbuild/linux-ia32@0.25.10': + optional: true + + '@esbuild/linux-loong64@0.25.0': + optional: true + + '@esbuild/linux-loong64@0.25.10': + optional: true + + '@esbuild/linux-mips64el@0.25.0': + optional: true + + '@esbuild/linux-mips64el@0.25.10': + optional: true + + '@esbuild/linux-ppc64@0.25.0': + optional: true + + '@esbuild/linux-ppc64@0.25.10': + optional: true + + '@esbuild/linux-riscv64@0.25.0': + optional: true + + '@esbuild/linux-riscv64@0.25.10': + optional: true + + '@esbuild/linux-s390x@0.25.0': + optional: true + + '@esbuild/linux-s390x@0.25.10': + optional: true + + '@esbuild/linux-x64@0.25.0': + optional: true + + '@esbuild/linux-x64@0.25.10': + optional: true + + '@esbuild/netbsd-arm64@0.25.0': + optional: true + + '@esbuild/netbsd-arm64@0.25.10': + optional: true + + '@esbuild/netbsd-x64@0.25.0': + optional: true + + '@esbuild/netbsd-x64@0.25.10': + optional: true + + '@esbuild/openbsd-arm64@0.25.0': + optional: true + + '@esbuild/openbsd-arm64@0.25.10': + optional: true + + '@esbuild/openbsd-x64@0.25.0': + optional: true + + '@esbuild/openbsd-x64@0.25.10': + optional: true + + '@esbuild/openharmony-arm64@0.25.10': + optional: true + + '@esbuild/sunos-x64@0.25.0': + optional: true + + '@esbuild/sunos-x64@0.25.10': + optional: true + + '@esbuild/win32-arm64@0.25.0': + optional: true + + '@esbuild/win32-arm64@0.25.10': + optional: true + + '@esbuild/win32-ia32@0.25.0': + optional: true + + '@esbuild/win32-ia32@0.25.10': + optional: true + + '@esbuild/win32-x64@0.25.0': + optional: true + + '@esbuild/win32-x64@0.25.10': + optional: true + '@eslint-community/eslint-utils@4.7.0(eslint@9.34.0(jiti@2.5.1))': dependencies: eslint: 9.34.0(jiti@2.5.1) @@ -16658,6 +18477,12 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) + '@floating-ui/react-dom@2.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@floating-ui/dom': 1.7.4 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + '@floating-ui/react-dom@2.1.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@floating-ui/dom': 1.7.4 @@ -16754,6 +18579,11 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@img/sharp-darwin-arm64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.1.0 + optional: true + '@img/sharp-darwin-arm64@0.34.2': optionalDependencies: '@img/sharp-libvips-darwin-arm64': 1.1.0 @@ -16764,6 +18594,11 @@ snapshots: '@img/sharp-libvips-darwin-arm64': 1.2.0 optional: true + '@img/sharp-darwin-x64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.1.0 + optional: true + '@img/sharp-darwin-x64@0.34.2': optionalDependencies: '@img/sharp-libvips-darwin-x64': 1.1.0 @@ -16828,6 +18663,11 @@ snapshots: '@img/sharp-libvips-linuxmusl-x64@1.2.0': optional: true + '@img/sharp-linux-arm64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.1.0 + optional: true + '@img/sharp-linux-arm64@0.34.2': optionalDependencies: '@img/sharp-libvips-linux-arm64': 1.1.0 @@ -16838,6 +18678,11 @@ snapshots: '@img/sharp-libvips-linux-arm64': 1.2.0 optional: true + '@img/sharp-linux-arm@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.1.0 + optional: true + '@img/sharp-linux-arm@0.34.2': optionalDependencies: '@img/sharp-libvips-linux-arm': 1.1.0 @@ -16853,6 +18698,11 @@ snapshots: '@img/sharp-libvips-linux-ppc64': 1.2.0 optional: true + '@img/sharp-linux-s390x@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.1.0 + optional: true + '@img/sharp-linux-s390x@0.34.2': optionalDependencies: '@img/sharp-libvips-linux-s390x': 1.1.0 @@ -16863,6 +18713,11 @@ snapshots: '@img/sharp-libvips-linux-s390x': 1.2.0 optional: true + '@img/sharp-linux-x64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.1.0 + optional: true + '@img/sharp-linux-x64@0.34.2': optionalDependencies: '@img/sharp-libvips-linux-x64': 1.1.0 @@ -16873,6 +18728,11 @@ snapshots: '@img/sharp-libvips-linux-x64': 1.2.0 optional: true + '@img/sharp-linuxmusl-arm64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 + optional: true + '@img/sharp-linuxmusl-arm64@0.34.2': optionalDependencies: '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 @@ -16883,6 +18743,11 @@ snapshots: '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 optional: true + '@img/sharp-linuxmusl-x64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 + optional: true + '@img/sharp-linuxmusl-x64@0.34.2': optionalDependencies: '@img/sharp-libvips-linuxmusl-x64': 1.1.0 @@ -16893,6 +18758,11 @@ snapshots: '@img/sharp-libvips-linuxmusl-x64': 1.2.0 optional: true + '@img/sharp-wasm32@0.34.1': + dependencies: + '@emnapi/runtime': 1.5.0 + optional: true + '@img/sharp-wasm32@0.34.2': dependencies: '@emnapi/runtime': 1.4.3 @@ -16909,12 +18779,18 @@ snapshots: '@img/sharp-win32-arm64@0.34.3': optional: true + '@img/sharp-win32-ia32@0.34.1': + optional: true + '@img/sharp-win32-ia32@0.34.2': optional: true '@img/sharp-win32-ia32@0.34.3': optional: true + '@img/sharp-win32-x64@0.34.1': + optional: true + '@img/sharp-win32-x64@0.34.2': optional: true @@ -16938,6 +18814,12 @@ snapshots: dependencies: '@swc/helpers': 0.5.17 + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -17538,6 +19420,13 @@ snapshots: '@lemonsqueezy/lemonsqueezy.js@4.0.0': {} + '@lottiefiles/dotlottie-react@0.13.3(react@19.0.0)': + dependencies: + '@lottiefiles/dotlottie-web': 0.42.0 + react: 19.0.0 + + '@lottiefiles/dotlottie-web@0.42.0': {} + '@makerkit/data-loader-supabase-core@0.0.10(@supabase/postgrest-js@1.19.4)(@supabase/supabase-js@2.49.4)': dependencies: '@supabase/postgrest-js': 1.19.4 @@ -17576,17 +19465,17 @@ snapshots: dependencies: react: 19.1.0 - '@medusajs/icons@2.10.2(react@19.0.0-rc-66855b96-20241106)': + '@medusajs/icons@2.10.3(react@19.0.0-rc-66855b96-20241106)': dependencies: react: 19.0.0-rc-66855b96-20241106 - '@medusajs/icons@2.10.2(react@19.1.0)': + '@medusajs/icons@2.10.3(react@19.1.0)': dependencies: react: 19.1.0 - '@medusajs/js-sdk@2.10.2(awilix@8.0.1)': + '@medusajs/js-sdk@2.10.3(awilix@8.0.1)': dependencies: - '@medusajs/types': 2.10.2(awilix@8.0.1) + '@medusajs/types': 2.10.3(awilix@8.0.1) fetch-event-stream: 0.1.5 qs: 6.14.0 transitivePeerDependencies: @@ -17594,29 +19483,29 @@ snapshots: - ioredis - vite - '@medusajs/types@2.10.2(awilix@8.0.1)': + '@medusajs/types@2.10.3(awilix@8.0.1)': dependencies: awilix: 8.0.1 bignumber.js: 9.3.0 - '@medusajs/ui-preset@2.10.2(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.9.2)))': + '@medusajs/ui-preset@2.10.3(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.9.2)))': dependencies: '@tailwindcss/forms': 0.5.10(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.9.2))) tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.9.2)) tailwindcss-animate: 1.0.7(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.9.2))) - '@medusajs/ui-preset@2.10.2(tailwindcss@4.1.7)': + '@medusajs/ui-preset@2.10.3(tailwindcss@4.1.7)': dependencies: '@tailwindcss/forms': 0.5.10(tailwindcss@4.1.7) tailwindcss: 4.1.7 tailwindcss-animate: 1.0.7(tailwindcss@4.1.7) - '@medusajs/ui@4.0.22(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)(typescript@5.9.2)': + '@medusajs/ui@4.0.23(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)(typescript@5.9.2)': dependencies: '@dnd-kit/core': 6.3.1(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106) - '@dnd-kit/sortable': 7.0.2(@dnd-kit/core@6.3.1(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106) + '@dnd-kit/sortable': 7.0.2(@dnd-kit/core@6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.0.0-rc-66855b96-20241106) '@dnd-kit/utilities': 3.2.2(react@19.0.0-rc-66855b96-20241106) - '@medusajs/icons': 2.10.2(react@19.0.0-rc-66855b96-20241106) + '@medusajs/icons': 2.10.3(react@19.0.0-rc-66855b96-20241106) '@radix-ui/react-dialog': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106) '@radix-ui/react-dismissable-layer': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106) '@tanstack/react-table': 8.20.5(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106) @@ -17638,12 +19527,12 @@ snapshots: - '@types/react-dom' - typescript - '@medusajs/ui@4.0.22(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.2)': + '@medusajs/ui@4.0.23(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.2)': dependencies: '@dnd-kit/core': 6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@dnd-kit/sortable': 7.0.2(@dnd-kit/core@6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) '@dnd-kit/utilities': 3.2.2(react@19.1.0) - '@medusajs/icons': 2.10.2(react@19.1.0) + '@medusajs/icons': 2.10.3(react@19.1.0) '@radix-ui/react-dialog': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-dismissable-layer': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tanstack/react-table': 8.20.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -18232,6 +20121,8 @@ snapshots: '@protobufjs/utf8@1.1.0': {} + '@radix-ui/colors@3.0.0': {} + '@radix-ui/number@1.1.0': {} '@radix-ui/number@1.1.1': {} @@ -18388,6 +20279,15 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-arrow@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-arrow@1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -18573,6 +20473,22 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-collapsible@1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-collection@1.1.1(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)': dependencies: '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.24)(react@19.0.0-rc-66855b96-20241106) @@ -18597,6 +20513,18 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-collection@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.2.0(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-collection@1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) @@ -18651,6 +20579,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.24 + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.0.10)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 @@ -18703,6 +20637,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.24 + '@radix-ui/react-context@1.1.2(@types/react@19.0.10)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@radix-ui/react-context@1.1.2(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 @@ -18837,6 +20777,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.24 + '@radix-ui/react-direction@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@radix-ui/react-direction@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 @@ -18908,6 +20854,19 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-dismissable-layer@1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-dismissable-layer@1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 @@ -18921,6 +20880,21 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-dropdown-menu@2.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-menu': 2.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -18978,6 +20952,12 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 + '@radix-ui/react-focus-guards@1.1.2(@types/react@19.0.10)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 @@ -19006,6 +20986,17 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-focus-scope@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) @@ -19104,6 +21095,13 @@ snapshots: optionalDependencies: '@types/react': 18.3.24 + '@radix-ui/react-id@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@radix-ui/react-id@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) @@ -19138,6 +21136,32 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-menu@2.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-focus-scope': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-popper': 1.2.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-portal': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.2.0(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0) + aria-hidden: 1.2.6 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react-remove-scroll: 2.7.1(@types/react@19.0.10)(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -19318,6 +21342,29 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-popover@1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-focus-scope': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-popper': 1.2.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-portal': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.2.0(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + aria-hidden: 1.2.6 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react-remove-scroll: 2.7.1(@types/react@19.0.10)(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -19423,6 +21470,24 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-popper@1.2.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-arrow': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/rect': 1.1.1 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-popper@1.2.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@floating-ui/react-dom': 2.1.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -19479,6 +21544,16 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-portal@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-portal@1.1.8(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -19519,6 +21594,16 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-presence@1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-presence@1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) @@ -19567,6 +21652,15 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-primitive@2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-slot': 1.2.0(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-primitive@2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) @@ -19729,6 +21823,23 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-roving-focus@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/number': 1.1.1 @@ -19946,6 +22057,13 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 + '@radix-ui/react-slot@1.2.0(@types/react@19.0.10)(react@19.0.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@radix-ui/react-slot@1.2.2(@types/react@19.1.4)(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) @@ -20060,6 +22178,22 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-tabs@1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-toast@1.2.15(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -20150,6 +22284,21 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-toggle-group@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-toggle': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-toggle@1.1.1(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)': dependencies: '@radix-ui/primitive': 1.1.1 @@ -20172,6 +22321,17 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-toggle@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-toolbar@1.1.1(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)': dependencies: '@radix-ui/primitive': 1.1.1 @@ -20242,6 +22402,26 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-tooltip@1.2.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-popper': 1.2.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-portal': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.2.0(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-visually-hidden': 1.2.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-tooltip@1.2.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 @@ -20274,6 +22454,12 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 @@ -20302,6 +22488,14 @@ snapshots: optionalDependencies: '@types/react': 18.3.24 + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.0.10)(react@19.0.0)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.4)(react@19.1.0)': dependencies: '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.4)(react@19.1.0) @@ -20317,6 +22511,13 @@ snapshots: optionalDependencies: '@types/react': 18.3.24 + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.0.10)(react@19.0.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.4)(react@19.1.0)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) @@ -20338,6 +22539,13 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) @@ -20370,6 +22578,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.24 + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 @@ -20408,6 +22622,13 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 + '@radix-ui/react-use-rect@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@radix-ui/react-use-rect@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: '@radix-ui/rect': 1.1.1 @@ -20429,6 +22650,13 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 + '@radix-ui/react-use-size@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@radix-ui/react-use-size@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) @@ -20454,6 +22682,15 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-visually-hidden@1.2.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-visually-hidden@1.2.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -22233,6 +24470,65 @@ snapshots: md-to-react-email: 5.0.5(react@19.1.0) react: 19.1.0 + '@react-email/preview-server@4.2.12(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.2)(postcss@8.5.6)(ts-node@10.9.2(@types/node@22.18.0)(typescript@5.9.2))': + dependencies: + '@babel/core': 7.26.10 + '@babel/parser': 7.27.0 + '@babel/traverse': 7.27.0 + '@lottiefiles/dotlottie-react': 0.13.3(react@19.0.0) + '@radix-ui/colors': 3.0.0 + '@radix-ui/react-collapsible': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-dropdown-menu': 2.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-popover': 1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.2.0(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-tabs': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-toggle-group': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-tooltip': 1.2.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@types/node': 22.14.1 + '@types/normalize-path': 3.0.2 + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/webpack': 5.28.5(esbuild@0.25.0) + autoprefixer: 10.4.21(postcss@8.5.6) + clsx: 2.1.1 + esbuild: 0.25.0 + framer-motion: 12.23.12(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + json5: 2.2.3 + log-symbols: 4.1.0 + module-punycode: punycode@2.3.1 + next: 15.5.2(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + node-html-parser: 7.0.1 + ora: 5.4.1 + pretty-bytes: 6.1.1 + prism-react-renderer: 2.4.1(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + sharp: 0.34.1 + socket.io-client: 4.8.1 + sonner: 2.0.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + source-map-js: 1.2.1 + spamc: 0.0.5 + stacktrace-parser: 0.1.11 + tailwind-merge: 3.2.0 + tailwindcss: 3.4.0(ts-node@10.9.2(@types/node@22.18.0)(typescript@5.9.2)) + use-debounce: 10.0.4(react@19.0.0) + zod: 3.24.3 + transitivePeerDependencies: + - '@emotion/is-prop-valid' + - '@opentelemetry/api' + - '@playwright/test' + - '@swc/core' + - babel-plugin-macros + - babel-plugin-react-compiler + - bufferutil + - postcss + - sass + - supports-color + - ts-node + - uglify-js + - utf-8-validate + - webpack-cli + '@react-email/preview@0.0.13(react@19.1.0)': dependencies: react: 19.1.0 @@ -23350,6 +25646,8 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 + '@socket.io/component-emitter@3.1.2': {} + '@standard-schema/utils@0.3.0': {} '@stripe/react-stripe-js@1.16.5(@stripe/stripe-js@1.54.2)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)': @@ -23615,6 +25913,10 @@ snapshots: dependencies: '@types/node': 22.18.0 + '@types/cors@2.8.19': + dependencies: + '@types/node': 22.18.0 + '@types/d3-array@3.2.1': {} '@types/d3-color@3.1.3': {} @@ -23721,6 +26023,10 @@ snapshots: '@types/node@17.0.21': {} + '@types/node@22.14.1': + dependencies: + undici-types: 6.21.0 + '@types/node@22.18.0': dependencies: undici-types: 6.21.0 @@ -23733,6 +26039,8 @@ snapshots: dependencies: '@types/node': 24.3.0 + '@types/normalize-path@3.0.2': {} + '@types/parse-json@4.0.2': {} '@types/pg-pool@2.0.6': @@ -23761,6 +26069,10 @@ snapshots: dependencies: '@types/react': 18.3.24 + '@types/react-dom@19.0.4(@types/react@19.0.10)': + dependencies: + '@types/react': 19.0.10 + '@types/react-dom@19.1.5(@types/react@19.1.4)': dependencies: '@types/react': 19.1.4 @@ -23781,6 +26093,10 @@ snapshots: '@types/prop-types': 15.7.15 csstype: 3.1.3 + '@types/react@19.0.10': + dependencies: + csstype: 3.1.3 + '@types/react@19.1.4': dependencies: csstype: 3.1.3 @@ -23797,6 +26113,17 @@ snapshots: '@types/unist@3.0.3': {} + '@types/webpack@5.28.5(esbuild@0.25.0)': + dependencies: + '@types/node': 22.18.0 + tapable: 2.2.3 + webpack: 5.101.3(esbuild@0.25.0) + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + - webpack-cli + '@types/ws@8.18.1': dependencies: '@types/node': 22.18.0 @@ -24187,6 +26514,11 @@ snapshots: '@xtuc/long@4.2.2': {} + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + acorn-import-assertions@1.9.0(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -24529,6 +26861,10 @@ snapshots: balanced-match@1.0.2: {} + base64-js@1.5.1: {} + + base64id@2.0.0: {} + big.js@5.2.2: {} bignumber.js@9.3.0: {} @@ -24543,6 +26879,12 @@ snapshots: binary-extensions@2.3.0: {} + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + boolbase@1.0.0: {} brace-expansion@1.1.11: @@ -24584,6 +26926,11 @@ snapshots: buffer-from@1.1.2: {} + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + busboy@1.6.0: dependencies: streamsearch: 1.1.0 @@ -24641,6 +26988,8 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@5.6.2: {} + char-regex@1.0.2: {} character-entities-html4@2.1.0: {} @@ -24663,6 +27012,10 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + chownr@3.0.0: {} chrome-trace-event@1.0.4: {} @@ -24671,6 +27024,10 @@ snapshots: ci-info@4.3.0: {} + citty@0.1.6: + dependencies: + consola: 3.4.2 + cjs-module-lexer@1.4.3: {} cjs-module-lexer@2.1.0: {} @@ -24679,6 +27036,16 @@ snapshots: dependencies: clsx: 2.1.1 + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-spinners@2.9.2: {} + client-only@0.0.1: {} cliui@8.0.1: @@ -24687,6 +27054,8 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + clone@1.0.4: {} + clsx@1.2.1: {} clsx@2.0.0: {} @@ -24721,13 +27090,11 @@ snapshots: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 - optional: true color@4.2.3: dependencies: color-convert: 2.0.1 color-string: 1.9.1 - optional: true colord@2.9.3: {} @@ -24739,6 +27106,8 @@ snapshots: commander@11.1.0: {} + commander@13.1.0: {} + commander@2.20.3: {} commander@4.1.1: {} @@ -24753,16 +27122,27 @@ snapshots: concat-map@0.0.1: {} + confbox@0.2.2: {} + + consola@3.4.2: {} + convert-source-map@1.9.0: {} convert-source-map@2.0.0: {} + cookie@0.7.2: {} + cookie@1.0.2: {} copy-to-clipboard@3.3.3: dependencies: toggle-selection: 1.0.6 + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + cosmiconfig@7.1.0: dependencies: '@types/parse-json': 4.0.2 @@ -24942,10 +27322,16 @@ snapshots: debounce@1.2.1: {} + debounce@2.2.0: {} + debug@3.2.7: dependencies: ms: 2.1.3 + debug@4.3.7: + dependencies: + ms: 2.1.3 + debug@4.4.1: dependencies: ms: 2.1.3 @@ -24966,6 +27352,10 @@ snapshots: deepmerge@4.3.1: {} + defaults@1.0.4: + dependencies: + clone: 1.0.4 + define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 @@ -25055,6 +27445,8 @@ snapshots: emittery@0.13.1: {} + emoji-regex@10.5.0: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -25065,6 +27457,36 @@ snapshots: dependencies: once: 1.4.0 + engine.io-client@6.6.3: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + engine.io-parser: 5.2.3 + ws: 8.17.1 + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + + engine.io@6.6.4: + dependencies: + '@types/cors': 2.8.19 + '@types/node': 22.18.0 + accepts: 1.3.8 + base64id: 2.0.0 + cookie: 0.7.2 + cors: 2.8.5 + debug: 4.3.7 + engine.io-parser: 5.2.3 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + enhanced-resolve@5.18.1: dependencies: graceful-fs: 4.2.11 @@ -25184,6 +27606,63 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + esbuild@0.25.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.0 + '@esbuild/android-arm': 0.25.0 + '@esbuild/android-arm64': 0.25.0 + '@esbuild/android-x64': 0.25.0 + '@esbuild/darwin-arm64': 0.25.0 + '@esbuild/darwin-x64': 0.25.0 + '@esbuild/freebsd-arm64': 0.25.0 + '@esbuild/freebsd-x64': 0.25.0 + '@esbuild/linux-arm': 0.25.0 + '@esbuild/linux-arm64': 0.25.0 + '@esbuild/linux-ia32': 0.25.0 + '@esbuild/linux-loong64': 0.25.0 + '@esbuild/linux-mips64el': 0.25.0 + '@esbuild/linux-ppc64': 0.25.0 + '@esbuild/linux-riscv64': 0.25.0 + '@esbuild/linux-s390x': 0.25.0 + '@esbuild/linux-x64': 0.25.0 + '@esbuild/netbsd-arm64': 0.25.0 + '@esbuild/netbsd-x64': 0.25.0 + '@esbuild/openbsd-arm64': 0.25.0 + '@esbuild/openbsd-x64': 0.25.0 + '@esbuild/sunos-x64': 0.25.0 + '@esbuild/win32-arm64': 0.25.0 + '@esbuild/win32-ia32': 0.25.0 + '@esbuild/win32-x64': 0.25.0 + + esbuild@0.25.10: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.10 + '@esbuild/android-arm': 0.25.10 + '@esbuild/android-arm64': 0.25.10 + '@esbuild/android-x64': 0.25.10 + '@esbuild/darwin-arm64': 0.25.10 + '@esbuild/darwin-x64': 0.25.10 + '@esbuild/freebsd-arm64': 0.25.10 + '@esbuild/freebsd-x64': 0.25.10 + '@esbuild/linux-arm': 0.25.10 + '@esbuild/linux-arm64': 0.25.10 + '@esbuild/linux-ia32': 0.25.10 + '@esbuild/linux-loong64': 0.25.10 + '@esbuild/linux-mips64el': 0.25.10 + '@esbuild/linux-ppc64': 0.25.10 + '@esbuild/linux-riscv64': 0.25.10 + '@esbuild/linux-s390x': 0.25.10 + '@esbuild/linux-x64': 0.25.10 + '@esbuild/netbsd-arm64': 0.25.10 + '@esbuild/netbsd-x64': 0.25.10 + '@esbuild/openbsd-arm64': 0.25.10 + '@esbuild/openbsd-x64': 0.25.10 + '@esbuild/openharmony-arm64': 0.25.10 + '@esbuild/sunos-x64': 0.25.10 + '@esbuild/win32-arm64': 0.25.10 + '@esbuild/win32-ia32': 0.25.10 + '@esbuild/win32-x64': 0.25.10 + escalade@3.2.0: {} escape-string-regexp@2.0.0: {} @@ -25201,7 +27680,7 @@ snapshots: eslint: 9.34.0(jiti@2.5.1) eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.34.0(jiti@2.5.1)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.5.1)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@2.5.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.34.0(jiti@2.5.1)) eslint-plugin-react: 7.37.5(eslint@9.34.0(jiti@2.5.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.34.0(jiti@2.5.1)) @@ -25237,21 +27716,22 @@ snapshots: tinyglobby: 0.2.14 unrs-resolver: 1.7.11 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.5.1)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@2.5.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.32.1(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.34.0(jiti@2.5.1)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.33.1(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@2.5.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.32.1(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/parser': 8.33.1(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) eslint: 9.34.0(jiti@2.5.1) eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.34.0(jiti@2.5.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.5.1)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@2.5.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -25262,7 +27742,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.34.0(jiti@2.5.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.32.1(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.34.0(jiti@2.5.1)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.33.1(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@2.5.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -25274,7 +27754,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.32.1(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/parser': 8.33.1(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -25521,6 +28001,8 @@ snapshots: jest-mock: 30.0.5 jest-util: 30.0.5 + exsolve@1.0.7: {} + facepaint@1.2.1: {} fast-copy@3.0.2: {} @@ -25642,6 +28124,15 @@ snapshots: fraction.js@4.3.7: {} + framer-motion@12.23.12(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + motion-dom: 12.23.21 + motion-utils: 12.23.6 + tslib: 2.8.1 + optionalDependencies: + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + fs.realpath@1.0.0: {} fsevents@2.3.3: @@ -25664,6 +28155,8 @@ snapshots: get-caller-file@2.0.5: {} + get-east-asian-width@1.4.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -25717,6 +28210,15 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + glob@11.0.3: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.1.1 + minimatch: 10.0.3 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.0 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -25792,6 +28294,8 @@ snapshots: dependencies: function-bind: 1.1.2 + he@1.2.0: {} + help-me@5.0.0: {} hoist-non-react-statics@3.3.2: @@ -25853,6 +28357,8 @@ snapshots: idb@7.1.1: {} + ieee754@1.2.1: {} + ignore@5.3.2: {} ignore@7.0.5: {} @@ -25934,8 +28440,7 @@ snapshots: is-arrayish@0.2.1: {} - is-arrayish@0.3.2: - optional: true + is-arrayish@0.3.2: {} is-async-function@2.1.1: dependencies: @@ -26008,6 +28513,10 @@ snapshots: is-hotkey@0.2.0: {} + is-interactive@1.0.0: {} + + is-interactive@2.0.0: {} + is-map@2.0.3: {} is-negative-zero@2.0.3: {} @@ -26055,6 +28564,12 @@ snapshots: dependencies: which-typed-array: 1.1.19 + is-unicode-supported@0.1.0: {} + + is-unicode-supported@1.3.0: {} + + is-unicode-supported@2.1.0: {} + is-weakmap@2.0.2: {} is-weakref@1.1.1: @@ -26148,6 +28663,10 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jackspeak@4.1.1: + dependencies: + '@isaacs/cliui': 8.0.2 + javascript-natural-sort@0.7.1: {} jest-changed-files@29.7.0: @@ -26779,6 +29298,8 @@ snapshots: jiti@1.21.7: {} + jiti@2.4.2: {} + jiti@2.5.1: {} joycon@3.1.1: {} @@ -26917,6 +29438,8 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.1 lightningcss-win32-x64-msvc: 1.30.1 + lilconfig@2.1.0: {} + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} @@ -26963,6 +29486,21 @@ snapshots: lodash@4.17.21: {} + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + log-symbols@6.0.0: + dependencies: + chalk: 5.6.2 + is-unicode-supported: 1.3.0 + + log-symbols@7.0.1: + dependencies: + is-unicode-supported: 2.1.0 + yoctocolors: 2.1.2 + long@5.3.2: {} longest-streak@3.1.0: {} @@ -26977,6 +29515,8 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.2.2: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -27388,14 +29928,26 @@ snapshots: mime-db@1.52.0: {} + mime-db@1.54.0: {} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + mimic-fn@2.1.0: {} + mimic-function@5.0.1: {} + mini-svg-data-uri@1.4.4: {} + minimatch@10.0.3: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -27422,6 +29974,12 @@ snapshots: module-details-from-path@1.0.4: {} + motion-dom@12.23.21: + dependencies: + motion-utils: 12.23.6 + + motion-utils@12.23.6: {} + mrmime@2.0.1: {} ms@2.1.3: {} @@ -27440,6 +29998,8 @@ snapshots: natural-compare@1.4.0: {} + negotiator@0.6.3: {} + neo-async@2.6.2: {} next-sitemap@4.2.3(next@15.3.2(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)): @@ -27482,6 +30042,31 @@ snapshots: - '@babel/core' - babel-plugin-macros + next@15.5.2(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + '@next/env': 15.5.2 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001723 + postcss: 8.4.31 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + styled-jsx: 5.1.6(@babel/core@7.26.10)(babel-plugin-macros@3.1.0)(react@19.0.0) + optionalDependencies: + '@next/swc-darwin-arm64': 15.5.2 + '@next/swc-darwin-x64': 15.5.2 + '@next/swc-linux-arm64-gnu': 15.5.2 + '@next/swc-linux-arm64-musl': 15.5.2 + '@next/swc-linux-x64-gnu': 15.5.2 + '@next/swc-linux-x64-musl': 15.5.2 + '@next/swc-win32-arm64-msvc': 15.5.2 + '@next/swc-win32-x64-msvc': 15.5.2 + '@opentelemetry/api': 1.9.0 + babel-plugin-react-compiler: 19.1.0-rc.2 + sharp: 0.34.3 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + next@15.5.2(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106): dependencies: '@next/env': 15.5.2 @@ -27524,6 +30109,11 @@ snapshots: fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 + node-html-parser@7.0.1: + dependencies: + css-select: 5.2.2 + he: 1.2.0 + node-int64@0.4.0: {} node-releases@2.0.19: {} @@ -27546,6 +30136,14 @@ snapshots: dependencies: boolbase: 1.0.0 + nypm@0.6.0: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + pathe: 2.0.3 + pkg-types: 2.3.0 + tinyexec: 0.3.2 + object-assign@4.1.1: {} object-hash@3.0.0: {} @@ -27600,6 +30198,10 @@ snapshots: dependencies: mimic-fn: 2.1.0 + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + openai@5.20.3(ws@8.18.2)(zod@4.1.5): optionalDependencies: ws: 8.18.2 @@ -27616,6 +30218,30 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + ora@8.2.0: + dependencies: + chalk: 5.6.2 + cli-cursor: 5.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.2.0 + strip-ansi: 7.1.0 + orderedmap@2.1.1: {} own-keys@1.0.1: @@ -27692,8 +30318,15 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-scurry@2.0.0: + dependencies: + lru-cache: 11.2.2 + minipass: 7.1.2 + path-type@4.0.0: {} + pathe@2.0.3: {} + peberminta@0.9.0: {} pg-cloudflare@1.2.7: @@ -27783,6 +30416,12 @@ snapshots: dependencies: find-up: 4.1.0 + pkg-types@2.3.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.7 + pathe: 2.0.3 + possible-typed-array-names@1.1.0: {} postcss-calc@10.1.1(postcss@8.5.6): @@ -27842,6 +30481,14 @@ snapshots: postcss: 8.5.6 ts-node: 10.9.2(@types/node@17.0.21)(typescript@5.9.2) + postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@22.18.0)(typescript@5.9.2)): + dependencies: + lilconfig: 3.1.3 + yaml: 2.8.0 + optionalDependencies: + postcss: 8.5.6 + ts-node: 10.9.2(@types/node@22.18.0)(typescript@5.9.2) + postcss-merge-longhand@7.0.5(postcss@8.5.6): dependencies: postcss: 8.5.6 @@ -28003,6 +30650,8 @@ snapshots: prettier@3.6.2: {} + pretty-bytes@6.1.1: {} + pretty-format@29.7.0: dependencies: '@jest/schemas': 29.6.3 @@ -28015,6 +30664,12 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 + prism-react-renderer@2.4.1(react@19.0.0): + dependencies: + '@types/prismjs': 1.26.5 + clsx: 2.1.1 + react: 19.0.0 + prism-react-renderer@2.4.1(react@19.0.0-rc-66855b96-20241106): dependencies: '@types/prismjs': 1.26.5 @@ -28361,6 +31016,11 @@ snapshots: date-fns: 4.1.0 react: 19.1.0 + react-dom@19.0.0(react@19.0.0): + dependencies: + react: 19.0.0 + scheduler: 0.25.0 + react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106): dependencies: react: 19.0.0-rc-66855b96-20241106 @@ -28371,6 +31031,29 @@ snapshots: react: 19.1.0 scheduler: 0.26.0 + react-email@4.2.12: + dependencies: + '@babel/parser': 7.28.3 + '@babel/traverse': 7.28.3 + chokidar: 4.0.3 + commander: 13.1.0 + debounce: 2.2.0 + esbuild: 0.25.10 + glob: 11.0.3 + jiti: 2.4.2 + log-symbols: 7.0.1 + mime-types: 3.0.1 + normalize-path: 3.0.0 + nypm: 0.6.0 + ora: 8.2.0 + prompts: 2.4.2 + socket.io: 4.8.1 + tsconfig-paths: 4.2.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + react-error-boundary@4.1.2(react@19.1.0): dependencies: '@babel/runtime': 7.27.6 @@ -28406,6 +31089,14 @@ snapshots: optionalDependencies: '@types/react': 18.3.24 + react-remove-scroll-bar@2.3.8(@types/react@19.0.10)(react@19.0.0): + dependencies: + react: 19.0.0 + react-style-singleton: 2.2.3(@types/react@19.0.10)(react@19.0.0) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.0.10 + react-remove-scroll-bar@2.3.8(@types/react@19.1.4)(react@19.1.0): dependencies: react: 19.1.0 @@ -28425,6 +31116,17 @@ snapshots: optionalDependencies: '@types/react': 18.3.24 + react-remove-scroll@2.7.1(@types/react@19.0.10)(react@19.0.0): + dependencies: + react: 19.0.0 + react-remove-scroll-bar: 2.3.8(@types/react@19.0.10)(react@19.0.0) + react-style-singleton: 2.2.3(@types/react@19.0.10)(react@19.0.0) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.0.10)(react@19.0.0) + use-sidecar: 1.1.3(@types/react@19.0.10)(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + react-remove-scroll@2.7.1(@types/react@19.1.4)(react@19.1.0): dependencies: react: 19.1.0 @@ -28512,6 +31214,14 @@ snapshots: optionalDependencies: '@types/react': 18.3.24 + react-style-singleton@2.2.3(@types/react@19.0.10)(react@19.0.0): + dependencies: + get-nonce: 1.0.1 + react: 19.0.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.0.10 + react-style-singleton@2.2.3(@types/react@19.1.4)(react@19.1.0): dependencies: get-nonce: 1.0.1 @@ -28533,6 +31243,8 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) + react@19.0.0: {} + react@19.0.0-rc-66855b96-20241106: {} react@19.1.0: {} @@ -28543,10 +31255,18 @@ snapshots: read-cmd-shim@5.0.0: {} + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + readdirp@3.6.0: dependencies: picomatch: 2.3.1 + readdirp@4.1.2: {} + real-require@0.2.0: {} recharts-scale@0.4.5: @@ -28630,6 +31350,16 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + reusify@1.1.0: {} rollup@4.35.0: @@ -28688,6 +31418,8 @@ snapshots: sax@1.4.1: {} + scheduler@0.25.0: {} + scheduler@0.25.0-rc-66855b96-20241106: {} scheduler@0.26.0: {} @@ -28751,6 +31483,33 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 + sharp@0.34.1: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.1 + '@img/sharp-darwin-x64': 0.34.1 + '@img/sharp-libvips-darwin-arm64': 1.1.0 + '@img/sharp-libvips-darwin-x64': 1.1.0 + '@img/sharp-libvips-linux-arm': 1.1.0 + '@img/sharp-libvips-linux-arm64': 1.1.0 + '@img/sharp-libvips-linux-ppc64': 1.1.0 + '@img/sharp-libvips-linux-s390x': 1.1.0 + '@img/sharp-libvips-linux-x64': 1.1.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 + '@img/sharp-linux-arm': 0.34.1 + '@img/sharp-linux-arm64': 0.34.1 + '@img/sharp-linux-s390x': 0.34.1 + '@img/sharp-linux-x64': 0.34.1 + '@img/sharp-linuxmusl-arm64': 0.34.1 + '@img/sharp-linuxmusl-x64': 0.34.1 + '@img/sharp-wasm32': 0.34.1 + '@img/sharp-win32-ia32': 0.34.1 + '@img/sharp-win32-x64': 0.34.1 + sharp@0.34.2: dependencies: color: 4.2.3 @@ -28853,7 +31612,6 @@ snapshots: simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 - optional: true sirv@2.0.4: dependencies: @@ -28891,6 +31649,47 @@ snapshots: is-plain-object: 5.0.0 tiny-warning: 1.0.3 + socket.io-adapter@2.5.5: + dependencies: + debug: 4.3.7 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-client@4.8.1: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + engine.io-client: 6.6.3 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + socket.io@4.8.1: + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + cors: 2.8.5 + debug: 4.3.7 + engine.io: 6.6.4 + socket.io-adapter: 2.5.5 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + sonic-boom@4.2.0: dependencies: atomic-sleep: 1.0.0 @@ -28905,6 +31704,11 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) + sonner@2.0.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + sonner@2.0.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 @@ -28926,6 +31730,8 @@ snapshots: source-map@0.6.1: {} + spamc@0.0.5: {} + split2@4.2.0: {} sprintf-js@1.0.3: {} @@ -28940,6 +31746,8 @@ snapshots: dependencies: type-fest: 0.7.1 + stdin-discarder@0.2.2: {} + stop-iteration-iterator@1.1.0: dependencies: es-errors: 1.3.0 @@ -28964,6 +31772,12 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 + string-width@7.2.0: + dependencies: + emoji-regex: 10.5.0 + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.0 + string.prototype.includes@2.0.1: dependencies: call-bind: 1.0.8 @@ -29014,6 +31828,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 @@ -29043,6 +31861,14 @@ snapshots: strnum@2.1.1: {} + styled-jsx@5.1.6(@babel/core@7.26.10)(babel-plugin-macros@3.1.0)(react@19.0.0): + dependencies: + client-only: 0.0.1 + react: 19.0.0 + optionalDependencies: + '@babel/core': 7.26.10 + babel-plugin-macros: 3.1.0 + styled-jsx@5.1.6(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react@19.0.0-rc-66855b96-20241106): dependencies: client-only: 0.0.1 @@ -29116,6 +31942,8 @@ snapshots: tailwind-merge@2.6.0: {} + tailwind-merge@3.2.0: {} + tailwind-merge@3.3.1: {} tailwindcss-animate@1.0.7(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.9.2))): @@ -29128,6 +31956,33 @@ snapshots: tailwindcss-radix@2.9.0: {} + tailwindcss@3.4.0(ts-node@10.9.2(@types/node@22.18.0)(typescript@5.9.2)): + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.3 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.7 + lilconfig: 2.1.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-import: 15.1.0(postcss@8.5.6) + postcss-js: 4.0.1(postcss@8.5.6) + postcss-load-config: 4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@22.18.0)(typescript@5.9.2)) + postcss-nested: 6.2.0(postcss@8.5.6) + postcss-selector-parser: 6.1.2 + resolve: 1.22.10 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.9.2)): dependencies: '@alloc/quick-lru': 5.2.0 @@ -29172,6 +32027,17 @@ snapshots: mkdirp: 3.0.1 yallist: 5.0.0 + terser-webpack-plugin@5.3.14(esbuild@0.25.0)(webpack@5.101.3(esbuild@0.25.0)): + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + jest-worker: 27.5.1 + schema-utils: 4.3.2 + serialize-javascript: 6.0.2 + terser: 5.42.0 + webpack: 5.101.3(esbuild@0.25.0) + optionalDependencies: + esbuild: 0.25.0 + terser-webpack-plugin@5.3.14(webpack@5.101.3): dependencies: '@jridgewell/trace-mapping': 0.3.25 @@ -29214,6 +32080,8 @@ snapshots: tiny-warning@1.0.3: {} + tinyexec@0.3.2: {} + tinyglobby@0.2.14: dependencies: fdir: 6.4.5(picomatch@4.0.2) @@ -29305,6 +32173,12 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + tslib@2.8.1: {} turbo-darwin-64@2.5.4: @@ -29500,6 +32374,13 @@ snapshots: optionalDependencies: '@types/react': 18.3.24 + use-callback-ref@1.3.3(@types/react@19.0.10)(react@19.0.0): + dependencies: + react: 19.0.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.0.10 + use-callback-ref@1.3.3(@types/react@19.1.4)(react@19.1.0): dependencies: react: 19.1.0 @@ -29507,6 +32388,10 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 + use-debounce@10.0.4(react@19.0.0): + dependencies: + react: 19.0.0 + use-sidecar@1.1.3(@types/react@18.3.24)(react@19.0.0-rc-66855b96-20241106): dependencies: detect-node-es: 1.1.0 @@ -29515,6 +32400,14 @@ snapshots: optionalDependencies: '@types/react': 18.3.24 + use-sidecar@1.1.3(@types/react@19.0.10)(react@19.0.0): + dependencies: + detect-node-es: 1.1.0 + react: 19.0.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.0.10 + use-sidecar@1.1.3(@types/react@19.1.4)(react@19.1.0): dependencies: detect-node-es: 1.1.0 @@ -29543,6 +32436,8 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 + vary@1.1.2: {} + vfile-message@4.0.2: dependencies: '@types/unist': 3.0.3 @@ -29578,6 +32473,10 @@ snapshots: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + web-streams-polyfill@3.3.3: {} web-vitals@3.5.2: {} @@ -29639,6 +32538,38 @@ snapshots: - esbuild - uglify-js + webpack@5.101.3(esbuild@0.25.0): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.25.0 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.1 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.2 + tapable: 2.2.2 + terser-webpack-plugin: 5.3.14(esbuild@0.25.0)(webpack@5.101.3(esbuild@0.25.0)) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 @@ -29730,8 +32661,12 @@ snapshots: ws@7.5.10: {} + ws@8.17.1: {} + ws@8.18.2: {} + xmlhttprequest-ssl@2.1.2: {} + xtend@4.0.2: {} y-prosemirror@1.3.5(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27): @@ -29782,6 +32717,8 @@ snapshots: yocto-queue@0.1.0: {} + yoctocolors@2.1.2: {} + yup@1.7.0: dependencies: property-expr: 2.0.6 @@ -29789,6 +32726,8 @@ snapshots: toposort: 2.0.2 type-fest: 2.19.0 + zod@3.24.3: {} + zod@4.1.5: {} zwitch@2.0.4: {} diff --git a/public/locales/en/account.json b/public/locales/en/account.json index e101985..a17036c 100644 --- a/public/locales/en/account.json +++ b/public/locales/en/account.json @@ -169,5 +169,8 @@ "updateAccountError": "Updating account details failed", "updateAccountPreferencesSuccess": "Account preferences updated", "updateAccountPreferencesError": "Updating account preferences failed", - "consents": "Consents" + "consents": "Consents", + "healthBenefitForm": { + "updateSuccess": "Health benefit updated" + } } diff --git a/public/locales/en/billing.json b/public/locales/en/billing.json index 88b2bbe..f8d12bc 100644 --- a/public/locales/en/billing.json +++ b/public/locales/en/billing.json @@ -121,7 +121,6 @@ "label": "Cart ({{ items }})" }, "pageTitle": "{{companyName}} budget", - "description": "Configure company budget..", "saveChanges": "Save changes", "healthBenefitForm": { "title": "Health benefit form", @@ -134,10 +133,10 @@ "monthly": "Monthly" }, "expensesOverview": { - "title": "Expenses overview 2025", - "monthly": "Expense per employee per month *", - "yearly": "Maximum expense per employee per year *", - "total": "Maximum expense per {{employeeCount}} employee(s) per year *", - "sum": "Total" + "title": "Health account budget overview 2025", + "employeeCount": "Health account users", + "managementFeeTotal": "Health account management fee {{managementFee}} per employee per month *", + "currentMonthUsageTotal": "Health account current month usage", + "total": "Health account budget total" } } diff --git a/public/locales/en/cart.json b/public/locales/en/cart.json index cdd45f5..8e471b5 100644 --- a/public/locales/en/cart.json +++ b/public/locales/en/cart.json @@ -61,7 +61,10 @@ "order": { "title": "Order", "promotionsTotal": "Promotions total", + "companyBenefitsTotal": "Company benefits total", "subtotal": "Subtotal", + "benefitsTotal": "Paid with benefits", + "montonioTotal": "Paid with Montonio", "total": "Total", "giftCard": "Gift card" }, @@ -76,7 +79,8 @@ "orderNumber": "Order number", "orderStatus": "Order status", "paymentStatus": "Payment status", - "discount": "Discount" + "discount": "Discount", + "paymentConfirmationLoading": "Payment confirmation..." }, "montonioCallback": { "title": "Montonio checkout", diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 45b9239..df13fa0 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -75,10 +75,10 @@ "orderAnalysis": "Order analysis", "orderHealthAnalysis": "Order health check", "account": "Account", - "members": "Members", + "companyMembers": "Manage employees", "billing": "Billing", - "dashboard": "Dashboard", - "settings": "Settings", + "companyDashboard": "Dashboard", + "companySettings": "Settings", "profile": "Profile", "pickTime": "Pick time", "preferences": "Preferences", diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json index c92f438..03079b6 100644 --- a/public/locales/en/dashboard.json +++ b/public/locales/en/dashboard.json @@ -17,9 +17,13 @@ "orderAnalysis": { "title": "Order analysis", "description": "Select an analysis to get started" + }, + "benefits": { + "title": "Your company benefits" } }, "recommendations": { - "title": "Medreport recommends" + "title": "Medreport recommends", + "validUntil": "Valid until {{date}}" } } diff --git a/public/locales/en/teams.json b/public/locales/en/teams.json index b015945..8af264f 100644 --- a/public/locales/en/teams.json +++ b/public/locales/en/teams.json @@ -1,6 +1,12 @@ { "home": { - "pageTitle": "Company Dashboard" + "pageTitle": "Company Dashboard", + "headerTitle": "{{companyName}} Health Dashboard", + "healthDetails": "Company Health Details", + "membersSettingsButtonTitle": "Manage Members", + "membersSettingsButtonDescription": "Add, edit, or remove members.", + "membersBillingButtonTitle": "Manage Billing", + "membersBillingButtonDescription": "Select how you want to distribute the budget between members." }, "settings": { "pageTitle": "Company Settings", @@ -18,6 +24,34 @@ "billing": { "pageTitle": "Company Billing" }, + "benefitStatistics": { + "budget": { + "title": "Company Health Account Balance", + "balance": "Budget Balance {{balance}}", + "volume": "Budget Volume", + "membersCount": "Members Count" + }, + "data": { + "reservations": "{{value}} services", + "analysis": "Analyses", + "doctorsAndSpecialists": "Doctors and Specialists", + "researches": "Researches", + "analysisPackages": "Health Analysis Packages", + "analysisPackagesCount": "{{value}} service usage", + "totalSum": "Total Sum", + "eclinic": "E-Clinic", + "currentMonthUsageTotal": "Current Month Usage" + } + }, + "healthDetails": { + "women": "Women", + "men": "Men", + "avgAge": "Average Age", + "bmi": "BMI", + "cholesterol": "Cholesterol", + "vitaminD": "Vitamin D", + "smokers": "Smokers" + }, "yourTeams": "Your Companies ({{teamsCount}})", "createTeam": "Create a Company", "creatingTeam": "Creating Company...", @@ -28,7 +62,7 @@ "youLabel": "You", "emailLabel": "Email", "roleLabel": "Role", - "primaryOwnerLabel": "Primary Admin", + "primaryOwnerLabel": "Manager", "joinedAtLabel": "Joined at", "invitedAtLabel": "Invited at", "inviteMembersPageSubheading": "Invite members to your Company", @@ -153,12 +187,14 @@ "signInWithDifferentAccountDescription": "If you wish to accept the invitation with a different account, please sign out and back in with the account you wish to use.", "acceptInvitationHeading": "Accept Invitation to join {{accountName}}", "acceptInvitationDescription": "You have been invited to join the company {{accountName}}. If you wish to accept the invitation, please click the button below.", - "continueAs": "Continue as {{email}}", + "continueAs": "Continue as {{fullName}}", "joinTeamAccount": "Join Company", "joiningTeam": "Joining company...", "leaveTeamInputLabel": "Please type LEAVE to confirm leaving the company.", "leaveTeamInputDescription": "By leaving the company, you will no longer have access to it.", "reservedNameError": "This name is reserved. Please choose a different one.", "specialCharactersError": "This name cannot contain special characters. Please choose a different one.", - "personalCode": "Personal Code" + "personalCode": "Personal Code", + "teamOwnerPersonalCodeLabel": "Owner's Personal Code", + "distributedBenefitsAmount": "Assigned benefits" } diff --git a/public/locales/et/account.json b/public/locales/et/account.json index bb89f86..4dee3e5 100644 --- a/public/locales/et/account.json +++ b/public/locales/et/account.json @@ -169,5 +169,8 @@ "updateAccountError": "Konto andmete uuendamine ebaõnnestus", "updateAccountPreferencesSuccess": "Konto eelistused uuendatud", "updateAccountPreferencesError": "Konto eelistused uuendamine ebaõnnestus", - "consents": "Nõusolekud" + "consents": "Nõusolekud", + "healthBenefitForm": { + "updateSuccess": "Tervisekonto andmed uuendatud" + } } diff --git a/public/locales/et/billing.json b/public/locales/et/billing.json index 14734c6..1abb884 100644 --- a/public/locales/et/billing.json +++ b/public/locales/et/billing.json @@ -121,11 +121,10 @@ "label": "Cart ({{ items }})" }, "pageTitle": "{{companyName}} eelarve", - "description": "Muuda ettevõtte eelarve seadistusi.", "saveChanges": "Salvesta muudatused", "healthBenefitForm": { "title": "Tervisetoetuse vorm", - "description": "Ettevõtte Tervisekassa toetus töötajale", + "description": "Ettevõtte tervisekonto seadistamine", "info": "* Hindadele lisanduvad riigipoolsed maksud" }, "occurrence": { @@ -134,10 +133,10 @@ "monthly": "Kord kuus" }, "expensesOverview": { - "title": "Kulude ülevaade 2025 aasta raames", - "monthly": "Kulu töötaja kohta kuus *", - "yearly": "Maksimaalne kulu inimese kohta kokku aastas *", - "total": "Maksimaalne kulu {{employeeCount}} töötaja kohta aastas *", - "sum": "Kokku" + "title": "Tervisekonto eelarve ülevaade 2025", + "employeeCount": "Tervisekonto kasutajate arv", + "managementFeeTotal": "Tervisekonto haldustasu {{managementFee}} kuus töötaja kohta*", + "currentMonthUsageTotal": "Tervisekonto jooksva kuu kasutus", + "total": "Tervisekonto eelarve maht kokku" } } diff --git a/public/locales/et/cart.json b/public/locales/et/cart.json index 5588193..8154d57 100644 --- a/public/locales/et/cart.json +++ b/public/locales/et/cart.json @@ -61,7 +61,10 @@ "order": { "title": "Tellimus", "promotionsTotal": "Soodustuse summa", + "companyBenefitsTotal": "Toetuse summa", "subtotal": "Vahesumma", + "benefitsTotal": "Tasutud tervisetoetusest", + "montonioTotal": "Tasutud Montonio'ga", "total": "Summa", "giftCard": "Kinkekaart" }, @@ -76,7 +79,8 @@ "orderNumber": "Tellimuse number", "orderStatus": "Tellimuse olek", "paymentStatus": "Makse olek", - "discount": "Soodus" + "discount": "Soodus", + "paymentConfirmationLoading": "Makse kinnitamine..." }, "montonioCallback": { "title": "Montonio makseprotsess", diff --git a/public/locales/et/common.json b/public/locales/et/common.json index f31c970..4006383 100644 --- a/public/locales/et/common.json +++ b/public/locales/et/common.json @@ -75,10 +75,10 @@ "orderAnalysis": "Telli analüüs", "orderHealthAnalysis": "Telli terviseuuring", "account": "Konto", - "members": "Liikmed", - "billing": "Arveldamine", - "dashboard": "Ülevaade", - "settings": "Seaded", + "companyMembers": "Töötajate haldamine", + "billing": "Eelarve", + "companyDashboard": "Ülevaade", + "companySettings": "Seaded", "profile": "Profiil", "pickTime": "Vali aeg", "preferences": "Eelistused", diff --git a/public/locales/et/dashboard.json b/public/locales/et/dashboard.json index d18c230..1b518f0 100644 --- a/public/locales/et/dashboard.json +++ b/public/locales/et/dashboard.json @@ -17,9 +17,14 @@ "orderAnalysis": { "title": "Telli analüüs", "description": "Telli endale sobiv analüüs" + }, + "benefits": { + "title": "Sinu Medreport konto seis", + "validUntil": "Kehtiv kuni {{date}}" } }, "recommendations": { - "title": "Medreport soovitab teile" + "title": "Medreport soovitab teile", + "validUntil": "Kehtiv kuni {{date}}" } } diff --git a/public/locales/et/teams.json b/public/locales/et/teams.json index 6b85800..884923d 100644 --- a/public/locales/et/teams.json +++ b/public/locales/et/teams.json @@ -1,7 +1,7 @@ { "home": { "pageTitle": "Ettevõtte ülevaade", - "headerTitle": "{{companyName}} Tervisekassa kokkuvõte", + "headerTitle": "{{companyName}} tervise ülevaade", "healthDetails": "Ettevõtte terviseandmed", "membersSettingsButtonTitle": "Halda töötajaid", "membersSettingsButtonDescription": "Lisa, muuda või eemalda töötajaid.", @@ -28,17 +28,19 @@ "budget": { "title": "Ettevõtte Tervisekassa seis", "balance": "Eelarve jääk {{balance}}", - "volume": "Eelarve maht {{volume}}" + "volume": "Eelarve maht", + "membersCount": "Töötajate arv" }, "data": { - "reservations": "{{value}} teenust", + "reservations": "{{value}} tellimus(t)", "analysis": "Analüüsid", "doctorsAndSpecialists": "Eriarstid ja spetsialistid", "researches": "Uuringud", - "healthResearchPlans": "Terviseuuringute paketid", - "serviceUsage": "{{value}} teenuse kasutust", - "serviceSum": "Teenuste summa", - "eclinic": "Digikliinik" + "analysisPackages": "Terviseuuringute paketid", + "analysisPackagesCount": "{{value}} tellimus(t)", + "totalSum": "Tellitud teenuste summa", + "eclinic": "Digikliinik", + "currentMonthUsageTotal": "Kasutatud eelarve" } }, "healthDetails": { @@ -60,7 +62,7 @@ "youLabel": "Sina", "emailLabel": "E-post", "roleLabel": "Roll", - "primaryOwnerLabel": "Peaadministraator", + "primaryOwnerLabel": "Haldur", "joinedAtLabel": "Liitus", "invitedAtLabel": "Kutsutud", "inviteMembersPageSubheading": "Kutsu töötajaid oma ettevõttesse", @@ -185,7 +187,7 @@ "signInWithDifferentAccountDescription": "Kui soovid kutse vastu võtta teise kontoga, logi välja ja tagasi sisse soovitud kontoga.", "acceptInvitationHeading": "Võta kutse vastu, et liituda ettevõttega {{accountName}}", "acceptInvitationDescription": "Sind on kutsutud liituma ettevõttega {{accountName}}. Kui soovid kutse vastu võtta, vajuta allolevat nuppu.", - "continueAs": "Jätka kui {{email}}", + "continueAs": "Jätka kui {{fullName}}", "joinTeamAccount": "Liitu ettevõttega", "joiningTeam": "Ettevõttega liitumine...", "leaveTeamInputLabel": "Palun kirjuta LEAVE kinnituseks, et ettevõttest lahkuda.", @@ -193,5 +195,6 @@ "reservedNameError": "See nimi on reserveeritud. Palun vali mõni teine.", "specialCharactersError": "Nimi ei tohi sisaldada erimärke. Palun vali mõni teine.", "personalCode": "Isikukood", - "teamOwnerPersonalCodeLabel": "Omaniku isikukood" + "teamOwnerPersonalCodeLabel": "Omaniku isikukood", + "distributedBenefitsAmount": "Väljastatud toetus" } diff --git a/public/locales/ru/account.json b/public/locales/ru/account.json index 9eca7c0..fe66124 100644 --- a/public/locales/ru/account.json +++ b/public/locales/ru/account.json @@ -169,5 +169,8 @@ "updateAccountError": "Не удалось обновить данные аккаунта", "updateAccountPreferencesSuccess": "Предпочтения аккаунта обновлены", "updateAccountPreferencesError": "Не удалось обновить предпочтения аккаунта", - "consents": "Согласия" + "consents": "Согласия", + "healthBenefitForm": { + "updateSuccess": "Данные о выгоде обновлены" + } } diff --git a/public/locales/ru/billing.json b/public/locales/ru/billing.json index 90cdbfe..399a75c 100644 --- a/public/locales/ru/billing.json +++ b/public/locales/ru/billing.json @@ -121,7 +121,6 @@ "label": "Корзина ({{ items }})" }, "pageTitle": "Бюджет {{companyName}}", - "description": "Измените настройки бюджета компании.", "saveChanges": "Сохранить изменения", "healthBenefitForm": { "title": "Форма здоровья", @@ -135,9 +134,9 @@ }, "expensesOverview": { "title": "Обзор расходов за 2025 год", - "monthly": "Расход на одного сотрудника в месяц *", - "yearly": "Максимальный расход на одного человека в год *", - "total": "Максимальный расход на {{employeeCount}} сотрудников в год *", - "sum": "Итого" + "employeeCount": "Сотрудники корпоративного фонда здоровья", + "managementFeeTotal": "Расходы на управление корпоративным фондом здоровья {{managementFee}} в месяц на одного сотрудника *", + "currentMonthUsageTotal": "Расходы на корпоративный фонд здоровья в текущем месяце", + "total": "Общая сумма расходов на корпоративный фонд здоровья" } } diff --git a/public/locales/ru/cart.json b/public/locales/ru/cart.json index 0287b70..d11ba44 100644 --- a/public/locales/ru/cart.json +++ b/public/locales/ru/cart.json @@ -61,7 +61,10 @@ "order": { "title": "Заказ", "promotionsTotal": "Скидка", + "companyBenefitsTotal": "Скидка компании", "subtotal": "Промежуточный итог", + "benefitsTotal": "Оплачено за счет выгод", + "montonioTotal": "Оплачено за счет Montonio", "total": "Сумма", "giftCard": "Подарочная карта" }, @@ -76,7 +79,8 @@ "orderNumber": "Номер заказа", "orderStatus": "Статус заказа", "paymentStatus": "Статус оплаты", - "discount": "Скидка" + "discount": "Скидка", + "paymentConfirmationLoading": "Ожидание подтверждения оплаты..." }, "montonioCallback": { "title": "Процесс оплаты Montonio", diff --git a/public/locales/ru/common.json b/public/locales/ru/common.json index ca97132..510b4a1 100644 --- a/public/locales/ru/common.json +++ b/public/locales/ru/common.json @@ -75,10 +75,10 @@ "orderAnalysis": "Заказать анализ", "orderHealthAnalysis": "Заказать обследование", "account": "Аккаунт", - "members": "Участники", + "companyMembers": "Участники", "billing": "Оплата", - "dashboard": "Обзор", - "settings": "Настройки", + "companyDashboard": "Обзор", + "companySettings": "Настройки", "profile": "Профиль", "pickTime": "Выбрать время", "preferences": "Предпочтения", diff --git a/public/locales/ru/dashboard.json b/public/locales/ru/dashboard.json index 9c0a7d6..523e70c 100644 --- a/public/locales/ru/dashboard.json +++ b/public/locales/ru/dashboard.json @@ -17,9 +17,14 @@ "orderAnalysis": { "title": "Заказать анализ", "description": "Закажите подходящий для вас анализ" + }, + "benefits": { + "title": "Ваш счет Medreport", + "validUntil": "Действителен до {{date}}" } }, "recommendations": { - "title": "Medreport recommends" + "title": "Medreport рекомендует", + "validUntil": "Действителен до {{date}}" } } diff --git a/public/locales/ru/teams.json b/public/locales/ru/teams.json index 9eaa712..74111f1 100644 --- a/public/locales/ru/teams.json +++ b/public/locales/ru/teams.json @@ -28,17 +28,19 @@ "budget": { "title": "Баланс Tervisekassa компании", "balance": "Остаток бюджета {{balance}}", - "volume": "Объем бюджета {{volume}}" + "volume": "Объем бюджета", + "membersCount": "Количество сотрудников" }, "data": { "reservations": "{{value}} услуги", "analysis": "Анализы", "doctorsAndSpecialists": "Врачи и специалисты", "researches": "Исследования", - "healthResearchPlans": "Пакеты медицинских исследований", - "serviceUsage": "{{value}} использование услуг", - "serviceSum": "Сумма услуг", - "eclinic": "Дигиклиника" + "analysisPackages": "Пакеты медицинских исследований", + "analysisPackagesCount": "{{value}} использование услуг", + "totalSum": "Сумма услуг", + "eclinic": "Дигиклиника", + "currentMonthUsageTotal": "Текущее использование бюджета" } }, "healthDetails": { @@ -185,7 +187,7 @@ "signInWithDifferentAccountDescription": "Если вы хотите принять приглашение с другим аккаунтом, выйдите из системы и войдите с нужным аккаунтом.", "acceptInvitationHeading": "Принять приглашение для присоединения к {{accountName}}", "acceptInvitationDescription": "Вас пригласили присоединиться к компании {{accountName}}. Чтобы принять приглашение, нажмите кнопку ниже.", - "continueAs": "Продолжить как {{email}}", + "continueAs": "Продолжить как {{fullName}}", "joinTeamAccount": "Присоединиться к компании", "joiningTeam": "Присоединение к компании...", "leaveTeamInputLabel": "Пожалуйста, введите LEAVE для подтверждения выхода из компании.", @@ -193,5 +195,6 @@ "reservedNameError": "Это имя зарезервировано. Пожалуйста, выберите другое.", "specialCharactersError": "Это имя не может содержать специальные символы. Пожалуйста, выберите другое.", "personalCode": "Идентификационный код", - "teamOwnerPersonalCodeLabel": "Идентификационный код владельца" + "teamOwnerPersonalCodeLabel": "Идентификационный код владельца", + "distributedBenefitsAmount": "Распределенные выплаты" } diff --git a/supabase/migrations/20250925153100_sync_connected_online_cron_job.sql b/supabase/migrations/20250925153100_sync_connected_online_cron_job.sql new file mode 100644 index 0000000..f3fde10 --- /dev/null +++ b/supabase/migrations/20250925153100_sync_connected_online_cron_job.sql @@ -0,0 +1,14 @@ +select + cron.schedule( + 'sync-connected-online-every-night', -- Unique job name + '0 1 * * *', -- Cron schedule: every night at 04:00 (GMT +3) + $$ + select + net.http_post( + url := 'https://test.medreport.ee/api/job/sync-connected-online', + headers := jsonb_build_object( + 'x-jobs-api-key', 'fd26ec26-70ed-11f0-9e95-431ac3b15a84' + ) + ) as request_id; + $$ + ); diff --git a/supabase/migrations/20250926040043_update_consume_balance.sql b/supabase/migrations/20250926040043_update_consume_balance.sql new file mode 100644 index 0000000..5f50c31 --- /dev/null +++ b/supabase/migrations/20250926040043_update_consume_balance.sql @@ -0,0 +1,59 @@ +alter table medreport.account_balance_entries add column "is_analysis_order" boolean; +alter table medreport.account_balance_entries add column "is_analysis_package_order" boolean; + +drop function if exists medreport.consume_account_balance(uuid, numeric, text, text); + +-- Create function to consume balance (for purchases) +create or replace function medreport.consume_account_balance( + p_account_id uuid, + p_amount numeric, + p_description text, + p_reference_id text default null, + p_is_analysis_order boolean default false, + p_is_analysis_package_order boolean default false +) +returns boolean +language plpgsql +security definer +as $$ +declare + current_balance numeric; + remaining_amount numeric := p_amount; + entry_record record; + consumed_amount numeric; +begin + -- Get current balance + current_balance := medreport.get_account_balance(p_account_id); + + -- Check if sufficient balance + if current_balance < p_amount then + return false; + end if; + + -- Record the consumption + insert into medreport.account_balance_entries ( + account_id, + amount, + entry_type, + description, + reference_id, + created_by, + is_analysis_order, + is_analysis_package_order + ) values ( + p_account_id, + -p_amount, + 'purchase', + p_description, + p_reference_id, + auth.uid(), + p_is_analysis_order, + p_is_analysis_package_order + ); + + return true; +end; +$$; + +-- Grant execute permission +grant execute on function medreport.consume_account_balance(uuid, numeric, text, text, boolean, boolean) to authenticated, service_role; diff --git a/supabase/migrations/20250926135946_include_benefit_config_id.sql b/supabase/migrations/20250926135946_include_benefit_config_id.sql new file mode 100644 index 0000000..cd6b448 --- /dev/null +++ b/supabase/migrations/20250926135946_include_benefit_config_id.sql @@ -0,0 +1,219 @@ +ALTER TABLE medreport.account_balance_entries ADD COLUMN benefit_distribution_schedule_id uuid; + +-- Also setting `benefit_distribution_schedule_id` value now +drop function if exists medreport.distribute_health_benefits(uuid, numeric, text); +create or replace function medreport.distribute_health_benefits( + p_benefit_distribution_schedule_id uuid +) +returns void +language plpgsql +security definer +as $$ +declare + member_record record; + expires_date timestamp with time zone; + v_company_id uuid; + v_benefit_amount numeric; +begin + -- Expires on first day of next year. + expires_date := date_trunc('year', now() + interval '1 year'); + + -- Get company_id and benefit_amount from benefit_distribution_schedule + select company_id, benefit_amount into v_company_id, v_benefit_amount + from medreport.benefit_distribution_schedule + where id = p_benefit_distribution_schedule_id; + + -- Get all personal accounts that are members of this company + for member_record in + select distinct a.id as personal_account_id + from medreport.accounts a + join medreport.accounts_memberships am on a.id = am.user_id + where am.account_id = v_company_id + and a.is_personal_account = true + loop + -- Check if there is already a balance entry for this personal account from the same company in same month + if exists ( + select 1 + from medreport.account_balance_entries + where entry_type = 'benefit' + and account_id = member_record.personal_account_id + and source_company_id = v_company_id + and date_trunc('month', created_at) = date_trunc('month', now()) + ) then + continue; + end if; + + -- Insert balance entry for each personal account + insert into medreport.account_balance_entries ( + account_id, + amount, + entry_type, + description, + source_company_id, + created_by, + expires_at, + benefit_distribution_schedule_id + ) values ( + member_record.personal_account_id, + v_benefit_amount, + 'benefit', + 'Health benefit from company', + v_company_id, + auth.uid(), + expires_date, + p_benefit_distribution_schedule_id + ); + end loop; +end; +$$; + +grant execute on function medreport.distribute_health_benefits(uuid) to authenticated, service_role; + +create or replace function medreport.process_periodic_benefit_distributions() +returns void +language plpgsql +as $$ +declare + schedule_record record; + next_distribution_date timestamp with time zone; +begin + -- Get all active schedules that are due for distribution + for schedule_record in + select * + from medreport.benefit_distribution_schedule + where is_active = true + and next_distribution_at <= now() + loop + -- Distribute benefits + perform medreport.distribute_health_benefits( + schedule_record.id + ); + + -- Calculate next distribution date + next_distribution_date := medreport.calculate_next_distribution_date( + schedule_record.benefit_occurrence, + now() + ); + + -- Update the schedule + update medreport.benefit_distribution_schedule + set + last_distributed_at = now(), + next_distribution_at = next_distribution_date, + updated_at = now() + where id = schedule_record.id; + end loop; +end; +$$; + +DROP FUNCTION IF EXISTS medreport.upsert_benefit_distribution_schedule(uuid,numeric,text); +create or replace function medreport.upsert_benefit_distribution_schedule( + p_company_id uuid, + p_benefit_amount numeric, + p_benefit_occurrence text +) +-- Return schedule row id +returns uuid +language plpgsql +as $$ +declare + next_distribution_date timestamp with time zone; + record_id uuid; +begin + -- Calculate next distribution date + next_distribution_date := medreport.calculate_next_distribution_date(p_benefit_occurrence); + + -- Check if there's an existing record for this company + select id into record_id + from medreport.benefit_distribution_schedule + where company_id = p_company_id + limit 1; + + if record_id is not null then + -- Update existing record + update medreport.benefit_distribution_schedule + set + benefit_amount = p_benefit_amount, + benefit_occurrence = p_benefit_occurrence, + next_distribution_at = next_distribution_date, + is_active = true, + updated_at = now() + where id = record_id; + else + record_id := gen_random_uuid(); + + -- Insert new record + insert into medreport.benefit_distribution_schedule ( + id, + company_id, + benefit_amount, + benefit_occurrence, + next_distribution_at + ) values ( + record_id, + p_company_id, + p_benefit_amount, + p_benefit_occurrence, + next_distribution_date + ); + end if; + + return record_id; +end; +$$; + +grant execute on function medreport.upsert_benefit_distribution_schedule(uuid, numeric, text) to authenticated, service_role; + +create or replace function medreport.trigger_distribute_benefits() +returns trigger +language plpgsql +security definer +as $$ +declare + v_benefit_distribution_schedule_id uuid; +begin + -- Only distribute if benefit_amount is set and greater than 0 + if new.benefit_amount is not null and new.benefit_amount > 0 then + -- Create or update the distribution schedule for future distributions + v_benefit_distribution_schedule_id := medreport.upsert_benefit_distribution_schedule( + new.account_id, + new.benefit_amount, + coalesce(new.benefit_occurance, 'yearly') + ); + + -- Distribute benefits to all company members immediately + perform medreport.distribute_health_benefits( + v_benefit_distribution_schedule_id + ); + else + -- If benefit_amount is 0 or null, deactivate the schedule + update medreport.benefit_distribution_schedule + set is_active = false, updated_at = now() + where company_id = new.account_id; + end if; + + return new; +end; +$$; + + +CREATE OR REPLACE FUNCTION medreport.get_benefits_usages_for_company_members(p_account_id uuid) +returns table ( + personal_account_id uuid, + benefit_amount numeric +) +language plpgsql +as $$ +begin + return query + select + abe.account_id as personal_account_id, + sum(abe.amount) as benefit_amount + from medreport.account_balance_entries abe + where abe.source_company_id = p_account_id + and abe.entry_type = 'benefit' + group by abe.account_id; +end; +$$; + +grant execute on function medreport.get_benefits_usages_for_company_members(uuid) to authenticated, service_role; diff --git a/supabase/migrations/20250929144800_tto_booking_email_hook.sql b/supabase/migrations/20250929144800_tto_booking_email_hook.sql new file mode 100644 index 0000000..edc5926 --- /dev/null +++ b/supabase/migrations/20250929144800_tto_booking_email_hook.sql @@ -0,0 +1,11 @@ +create trigger tto_order_confirmation_email +after update on medreport.connected_online_reservation +for each row +when (old.status is distinct from new.status and new.status = 'CONFIRMED') +execute function supabase_functions.http_request( + 'http://host.docker.internal:3000/api/db/webhook', + 'POST', + '{"Content-Type":"application/json", "X-Supabase-Event-Signature":"WEBHOOKSECRET"}', + '{}', + '1000' +); \ No newline at end of file