- {analysisElementsWithResults.map(({ results }) => {
- const analysisElement = analysisElements.find(
- (element) =>
- element.analysis_id_original ===
- results.analysis_element_original_id,
- );
- if (!analysisElement) {
- return null;
- }
+
+ {analysisOrders.length > 0 && analysisElements.length > 0 ? analysisOrders.map((analysisOrder) => {
+ const analysisResponse = analysisResponses?.find((response) => response.analysis_order_id === analysisOrder.id);
+ const analysisElementIds = getAnalysisElementIds([analysisOrder]);
+ const analysisElementsForOrder = analysisElements.filter((element) => analysisElementIds.includes(element.id));
return (
-
+
+
+
+
+
+
+
+
+
+ {analysisElementsForOrder.length > 0 ? analysisElementsForOrder.map((analysisElement) => {
+ const results = analysisResponse?.elements.some((element) => element.analysis_element_original_id === analysisElement.analysis_id_original)
+ && analysisResponseElements?.find((element) => element.analysis_element_original_id === analysisElement.analysis_id_original);
+ if (!results) {
+ return (
+
+ );
+ }
+ return (
+
+ );
+ }) : (
+
+
+
+ )}
+
+
);
- })}
- {analysisElementsWithoutResults.map((element) => (
-
- ))}
- {hasNoAnalysisElements && (
+ }) : (
-
+
)}
diff --git a/app/home/(user)/(dashboard)/cart/montonio-callback/actions.ts b/app/home/(user)/(dashboard)/cart/montonio-callback/actions.ts
index bede603..5ee25aa 100644
--- a/app/home/(user)/(dashboard)/cart/montonio-callback/actions.ts
+++ b/app/home/(user)/(dashboard)/cart/montonio-callback/actions.ts
@@ -9,6 +9,9 @@ import { placeOrder, retrieveCart } from "@lib/data/cart";
import { createI18nServerInstance } from "~/lib/i18n/i18n.server";
import { createOrder } from '~/lib/services/order.service';
import { getOrderedAnalysisElementsIds, sendOrderToMedipost } from '~/lib/services/medipost.service';
+import { createNotificationsApi } from '@kit/notifications/api';
+import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client';
+import { AccountWithParams } from '@kit/accounts/api';
const ANALYSIS_PACKAGES_TYPE_HANDLE = 'analysis-packages';
const MONTONIO_PAID_STATUS = 'PAID';
@@ -31,7 +34,22 @@ const env = () => z
siteUrl: process.env.NEXT_PUBLIC_SITE_URL!,
});
-const sendEmail = async ({ email, analysisPackageName, personName, partnerLocationName, language }: { email: string, analysisPackageName: string, personName: string, partnerLocationName: string, language: string }) => {
+const sendEmail = async ({
+ account,
+ email,
+ analysisPackageName,
+ personName,
+ partnerLocationName,
+ language,
+}: {
+ account: AccountWithParams,
+ email: string,
+ analysisPackageName: string,
+ personName: string,
+ partnerLocationName: string,
+ language: string,
+}) => {
+ const client = getSupabaseServerAdminClient();
try {
const { renderSynlabAnalysisPackageEmail } = await import('@kit/email-templates');
const { getMailer } = await import('@kit/mailers');
@@ -55,6 +73,11 @@ const sendEmail = async ({ email, analysisPackageName, personName, partnerLocati
.catch((error) => {
throw new Error(`Failed to send email, message=${error}`);
});
+ await createNotificationsApi(client)
+ .createNotification({
+ account_id: account.id,
+ body: html,
+ });
} catch (error) {
throw new Error(`Failed to send email, message=${error}`);
}
@@ -62,13 +85,13 @@ const sendEmail = async ({ email, analysisPackageName, personName, partnerLocati
export async function processMontonioCallback(orderToken: string) {
const { language } = await createI18nServerInstance();
-
+
const secretKey = process.env.MONTONIO_SECRET_KEY as string;
const decoded = jwt.verify(orderToken, secretKey, {
algorithms: ['HS256'],
}) as MontonioOrderToken;
-
+
if (decoded.paymentStatus !== MONTONIO_PAID_STATUS) {
throw new Error("Payment not successful");
}
@@ -79,7 +102,7 @@ export async function processMontonioCallback(orderToken: string) {
}
try {
- const [,, cartId] = decoded.merchantReferenceDisplay.split(':');
+ const [, , cartId] = decoded.merchantReferenceDisplay.split(':');
if (!cartId) {
throw new Error("Cart ID not found");
}
@@ -89,6 +112,7 @@ export async function processMontonioCallback(orderToken: string) {
throw new Error("Cart not found");
}
+
const medusaOrder = await placeOrder(cartId, { revalidateCacheTags: false });
const orderedAnalysisElements = await getOrderedAnalysisElementsIds({ medusaOrder });
const orderId = await createOrder({ medusaOrder, orderedAnalysisElements });
@@ -96,7 +120,7 @@ export async function processMontonioCallback(orderToken: string) {
const { productTypes } = await listProductTypes();
const analysisPackagesType = productTypes.find(({ metadata }) => metadata?.handle === ANALYSIS_PACKAGES_TYPE_HANDLE);
const analysisPackageOrderItem = medusaOrder.items?.find(({ product_type_id }) => product_type_id === analysisPackagesType?.id);
-
+
const orderResult = {
medusaOrderId: medusaOrder.id,
email: medusaOrder.email,
@@ -107,10 +131,10 @@ export async function processMontonioCallback(orderToken: string) {
const { medusaOrderId, email, partnerLocationName, analysisPackageName } = orderResult;
const personName = account.name;
-
+
if (email && analysisPackageName) {
try {
- await sendEmail({ email, analysisPackageName, personName, partnerLocationName, language });
+ await sendEmail({ account, email, analysisPackageName, personName, partnerLocationName, language });
} catch (error) {
console.error("Failed to send email", error);
}
@@ -118,9 +142,9 @@ export async function processMontonioCallback(orderToken: string) {
// @TODO send email for separate analyses
console.error("Missing email or analysisPackageName", orderResult);
}
-
+
sendOrderToMedipost({ medusaOrderId, orderedAnalysisElements });
-
+
return { success: true, orderId };
} catch (error) {
console.error("Failed to place order", error);
diff --git a/app/home/(user)/(dashboard)/cart/page.tsx b/app/home/(user)/(dashboard)/cart/page.tsx
index 4e96c49..8eed4f6 100644
--- a/app/home/(user)/(dashboard)/cart/page.tsx
+++ b/app/home/(user)/(dashboard)/cart/page.tsx
@@ -32,11 +32,12 @@ export default async function CartPage() {
const otherItemsSorted = otherItems.sort((a, b) => (a.updated_at ?? "") > (b.updated_at ?? "") ? -1 : 1);
const item = otherItemsSorted[0];
+ const hasItemsWithTimer = false as boolean;
return (
}>
- {item && item.updated_at && }
+ {hasItemsWithTimer && item && item.updated_at && }
diff --git a/app/home/(user)/(dashboard)/order-analysis/page.tsx b/app/home/(user)/(dashboard)/order-analysis/page.tsx
index 942f78e..6358f18 100644
--- a/app/home/(user)/(dashboard)/order-analysis/page.tsx
+++ b/app/home/(user)/(dashboard)/order-analysis/page.tsx
@@ -5,6 +5,8 @@ import { Trans } from '@kit/ui/trans';
import { HomeLayoutPageHeader } from '../../_components/home-page-header';
import { loadAnalyses } from '../../_lib/server/load-analyses';
import OrderAnalysesCards from '../../_components/order-analyses-cards';
+import { createPageViewLog, PAGE_VIEW_ACTION } from '~/lib/services/audit/pageView.service';
+import { loadCurrentUserAccount } from '../../_lib/server/load-user-account';
export const generateMetadata = async () => {
const { t } = await createI18nServerInstance();
@@ -15,8 +17,18 @@ export const generateMetadata = async () => {
};
async function OrderAnalysisPage() {
+ const account = await loadCurrentUserAccount();
+ if (!account) {
+ throw new Error('Account not found');
+ }
+
const { analyses, countryCode } = await loadAnalyses();
+ await createPageViewLog({
+ accountId: account.id,
+ action: PAGE_VIEW_ACTION.VIEW_ORDER_ANALYSIS,
+ });
+
return (
<>
metadata?.handle === 'analysis-packages');
- const analysisPackageOrders: IOrderLineItem[] = medusaOrders.flatMap(({ id, items, payment_status, fulfillment_status }) => items
- ?.filter((item) => item.product_type_id === analysisPackagesType?.id)
- .map((item) => {
- const localOrder = analysisOrders.find((order) => order.medusa_order_id === id);
- if (!localOrder) {
- return null;
- }
- return {
- item,
- medusaOrderId: id,
- orderId: localOrder?.id,
- orderStatus: localOrder.status,
- analysis_element_ids: localOrder.analysis_element_ids,
- }
- })
- .filter((order) => order !== null)
- || []);
-
- const otherOrders: IOrderLineItem[] = medusaOrders
- .filter(({ items }) => items?.some((item) => item.product_type_id !== analysisPackagesType?.id))
- .flatMap(({ id, items, payment_status, fulfillment_status }) => items
- ?.map((item) => {
- const analysisOrder = analysisOrders.find((order) => order.medusa_order_id === id);
- if (!analysisOrder) {
- return null;
- }
- return {
- item,
- medusaOrderId: id,
- orderId: analysisOrder.id,
- orderStatus: analysisOrder.status,
- }
- })
- .filter((order) => order !== null)
- || []);
+ const analysisPackagesType = productTypes.find(({ metadata }) => metadata?.handle === 'analysis-packages')!;
return (
<>
@@ -73,8 +39,27 @@ async function OrdersPage() {
description={}
/>
-
-
+ {analysisOrders.map((analysisOrder) => {
+ const medusaOrder = medusaOrders.find(({ id }) => id === analysisOrder.medusa_order_id);
+ if (!medusaOrder) {
+ return null;
+ }
+
+ const medusaOrderItems = medusaOrder.items || [];
+ const medusaOrderItemsAnalysisPackages = medusaOrderItems.filter((item) => item.product_type_id === analysisPackagesType?.id);
+ const medusaOrderItemsOther = medusaOrderItems.filter((item) => item.product_type_id !== analysisPackagesType?.id);
+
+ return (
+
+
+
+
+ )
+ })}
>
);
diff --git a/app/home/(user)/(dashboard)/page.tsx b/app/home/(user)/(dashboard)/page.tsx
index 2f8b287..14d8bff 100644
--- a/app/home/(user)/(dashboard)/page.tsx
+++ b/app/home/(user)/(dashboard)/page.tsx
@@ -8,6 +8,7 @@ import { toTitleCase } from '@/lib/utils';
import Dashboard from '../_components/dashboard';
import { loadCurrentUserAccount } from '../_lib/server/load-user-account';
+import DashboardCards from '../_components/dashboard-cards';
export const generateMetadata = async () => {
const i18n = await createI18nServerInstance();
@@ -26,6 +27,7 @@ async function UserHomePage() {
return (
<>
+
diff --git a/app/home/(user)/_components/cart/cart-item-delete.tsx b/app/home/(user)/_components/cart/cart-item-delete.tsx
index 4c359cf..be67593 100644
--- a/app/home/(user)/_components/cart/cart-item-delete.tsx
+++ b/app/home/(user)/_components/cart/cart-item-delete.tsx
@@ -5,8 +5,8 @@ import { useState } from "react";
import { useTranslation } from "react-i18next";
import { toast } from 'sonner';
-import { deleteLineItem } from "@lib/data/cart";
import { Spinner } from "@medusajs/icons";
+import { handleDeleteCartItem } from "~/lib/services/medusaCart.service";
const CartItemDelete = ({
id,
@@ -22,7 +22,7 @@ const CartItemDelete = ({
setIsDeleting(true);
const promise = async () => {
- await deleteLineItem(id);
+ await handleDeleteCartItem({ lineId: id });
};
toast.promise(promise, {
diff --git a/app/home/(user)/_components/dashboard-cards.tsx b/app/home/(user)/_components/dashboard-cards.tsx
new file mode 100644
index 0000000..a24e465
--- /dev/null
+++ b/app/home/(user)/_components/dashboard-cards.tsx
@@ -0,0 +1,45 @@
+import { Trans } from '@kit/ui/trans';
+import {
+ Card,
+ CardHeader,
+ CardDescription,
+ CardFooter,
+} from '@kit/ui/card';
+
+import Link from 'next/link';
+import { Button } from '@kit/ui/button';
+import { ChevronRight, HeartPulse } from 'lucide-react';
+
+export default function DashboardCards() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/app/home/(user)/_components/order-analyses-cards.tsx b/app/home/(user)/_components/order-analyses-cards.tsx
index 9a9d8d8..f7575bc 100644
--- a/app/home/(user)/_components/order-analyses-cards.tsx
+++ b/app/home/(user)/_components/order-analyses-cards.tsx
@@ -7,11 +7,14 @@ import {
Card,
CardHeader,
CardFooter,
+ CardDescription,
} from '@kit/ui/card';
import { StoreProduct, StoreProductVariant } from '@medusajs/types';
import { useState } from 'react';
import { handleAddToCart } from '~/lib/services/medusaCart.service';
import { useRouter } from 'next/navigation';
+import { InfoTooltip } from '~/components/ui/info-tooltip';
+import { Trans } from '@kit/ui/trans';
export default function OrderAnalysesCards({
analyses,
@@ -21,7 +24,7 @@ export default function OrderAnalysesCards({
countryCode: string;
}) {
const router = useRouter();
-
+
const [isAddingToCart, setIsAddingToCart] = useState(false);
const handleSelect = async (selectedVariant: StoreProductVariant) => {
if (!selectedVariant?.id || isAddingToCart) return null
@@ -44,37 +47,62 @@ export default function OrderAnalysesCards({
{analyses.map(({
title,
- variants
- }) => (
-
-
-
-
+ {isAvailable && (
+
+
+
+ )}
+
+
+
+ {title}
+ {description && (
+ <>
+ {' '}
+
+ >
+ )}
+
+ {isAvailable && subtitle && (
+
+ {subtitle}
+
+ )}
+ {!isAvailable && (
+
+
+
+ )}
+
+
+ );
+ })}
);
}
diff --git a/app/home/(user)/_components/orders/order-block.tsx b/app/home/(user)/_components/orders/order-block.tsx
new file mode 100644
index 0000000..077d761
--- /dev/null
+++ b/app/home/(user)/_components/orders/order-block.tsx
@@ -0,0 +1,36 @@
+import { AnalysisOrder } from "~/lib/services/order.service";
+import { Trans } from '@kit/ui/makerkit/trans';
+import { StoreOrderLineItem } from "@medusajs/types";
+import OrderItemsTable from "./order-items-table";
+import Link from "next/link";
+import { Eye } from "lucide-react";
+
+export default function OrderBlock({ analysisOrder, itemsAnalysisPackage, itemsOther }: {
+ analysisOrder: AnalysisOrder,
+ itemsAnalysisPackage: StoreOrderLineItem[],
+ itemsOther: StoreOrderLineItem[],
+}) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/app/home/(user)/_components/orders/order-items-table.tsx b/app/home/(user)/_components/orders/order-items-table.tsx
new file mode 100644
index 0000000..48d502a
--- /dev/null
+++ b/app/home/(user)/_components/orders/order-items-table.tsx
@@ -0,0 +1,77 @@
+import { Trans } from '@kit/ui/trans';
+import {
+ Table,
+ TableBody,
+ TableHead,
+ TableRow,
+ TableHeader,
+ TableCell,
+} from '@kit/ui/table';
+import { StoreOrderLineItem } from "@medusajs/types";
+import { AnalysisOrder } from '~/lib/services/order.service';
+import { formatDate } from 'date-fns';
+import Link from 'next/link';
+import { Eye } from 'lucide-react';
+
+export default function OrderItemsTable({ items, title, analysisOrder }: {
+ items: StoreOrderLineItem[];
+ title: string;
+ analysisOrder: AnalysisOrder;
+}) {
+ if (!items || items.length === 0) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {items
+ .sort((a, b) => (a.created_at ?? "") > (b.created_at ?? "") ? -1 : 1)
+ .map((orderItem) => (
+
+
+
+ {orderItem.product_title}
+
+
+
+
+ {formatDate(orderItem.created_at, 'dd.MM.yyyy HH:mm')}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/app/home/(user)/_components/orders/orders-item.tsx b/app/home/(user)/_components/orders/orders-item.tsx
deleted file mode 100644
index ea8943d..0000000
--- a/app/home/(user)/_components/orders/orders-item.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import {
- TableCell,
- TableRow,
-} from '@kit/ui/table';
-import { Eye } from "lucide-react";
-import Link from "next/link";
-import { formatDate } from "date-fns";
-import { IOrderLineItem } from "./types";
-import { Trans } from '@kit/ui/trans';
-
-export default function OrdersItem({ orderItem }: {
- orderItem: IOrderLineItem,
-}) {
- return (
-
-
-
- {orderItem.item.product_title}
-
-
-
-
- {formatDate(orderItem.item.created_at, 'dd.MM.yyyy HH:mm')}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
diff --git a/app/home/(user)/_components/orders/orders-table.tsx b/app/home/(user)/_components/orders/orders-table.tsx
deleted file mode 100644
index 18f1872..0000000
--- a/app/home/(user)/_components/orders/orders-table.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import { Trans } from '@kit/ui/trans';
-import {
- Table,
- TableBody,
- TableHead,
- TableRow,
- TableHeader,
-} from '@kit/ui/table';
-import OrdersItem from "./orders-item";
-import { IOrderLineItem } from "./types";
-
-export default function OrdersTable({ orderItems, title }: {
- orderItems: IOrderLineItem[];
- title: string;
-}) {
- if (!orderItems || orderItems.length === 0) {
- return null;
- }
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {orderItems
- .sort((a, b) => (a.item.created_at ?? "") > (b.item.created_at ?? "") ? -1 : 1)
- .map((orderItem) => ())}
-
-
- )
-}
diff --git a/app/home/(user)/_components/orders/types.ts b/app/home/(user)/_components/orders/types.ts
deleted file mode 100644
index 2c7140e..0000000
--- a/app/home/(user)/_components/orders/types.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { StoreOrderLineItem } from "@medusajs/types";
-
-export interface IOrderLineItem {
- item: StoreOrderLineItem;
- medusaOrderId: string;
- orderId: number;
- orderStatus: string;
-}
diff --git a/app/home/[account]/_components/team-account-layout-sidebar.tsx b/app/home/[account]/_components/team-account-layout-sidebar.tsx
index 8f53ef4..4208b62 100644
--- a/app/home/[account]/_components/team-account-layout-sidebar.tsx
+++ b/app/home/[account]/_components/team-account-layout-sidebar.tsx
@@ -1,5 +1,6 @@
import type { User } from '@supabase/supabase-js';
+import { ApplicationRole } from '@kit/accounts/types/accounts';
import {
Sidebar,
SidebarContent,
@@ -18,6 +19,7 @@ type AccountModel = {
label: string | null;
value: string | null;
image: string | null;
+ application_role: ApplicationRole | null;
};
export function TeamAccountLayoutSidebar(props: {
diff --git a/app/home/[account]/_components/team-account-navigation-menu.tsx b/app/home/[account]/_components/team-account-navigation-menu.tsx
index c09a19a..80c1bb9 100644
--- a/app/home/[account]/_components/team-account-navigation-menu.tsx
+++ b/app/home/[account]/_components/team-account-navigation-menu.tsx
@@ -1,7 +1,4 @@
-import {
- BorderedNavigationMenu,
- BorderedNavigationMenuItem,
-} from '@kit/ui/bordered-navigation-menu';
+import { useMemo } from 'react';
import { AppLogo } from '~/components/app-logo';
import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container';
@@ -10,18 +7,22 @@ import { getTeamAccountSidebarConfig } from '~/config/team-account-navigation.co
// local imports
import { TeamAccountWorkspace } from '../_lib/server/team-account-workspace.loader';
import { TeamAccountNotifications } from './team-account-notifications';
-import { useMemo } from 'react';
export function TeamAccountNavigationMenu(props: {
workspace: TeamAccountWorkspace;
}) {
const { account, user, accounts: rawAccounts } = props.workspace;
- const accounts = useMemo(() => rawAccounts.map((account) => ({
- label: account.name,
- value: account.slug,
- image: account.picture_url,
- })),[rawAccounts])
+ const accounts = useMemo(
+ () =>
+ rawAccounts.map((account) => ({
+ label: account.name,
+ value: account.slug,
+ image: account.picture_url,
+ application_role: account.application_role,
+ })),
+ [rawAccounts],
+ );
const routes = getTeamAccountSidebarConfig(account.slug).routes.reduce<
Array<{
@@ -48,7 +49,7 @@ export function TeamAccountNavigationMenu(props: {