Merge branch 'main' into MED-57
This commit is contained in:
54
app/api/job/test-medipost-responses/route.ts
Normal file
54
app/api/job/test-medipost-responses/route.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getAnalysisOrdersAdmin } from "~/lib/services/order.service";
|
||||
import { composeOrderTestResponseXML, sendPrivateMessageTestResponse } from "~/lib/services/medipostTest.service";
|
||||
import { retrieveOrder } from "@lib/data";
|
||||
import { getAccountAdmin } from "~/lib/services/account.service";
|
||||
import { getOrderedAnalysisElementsIds } from "~/lib/services/medipost.service";
|
||||
import loadEnv from "../handler/load-env";
|
||||
import validateApiKey from "../handler/validate-api-key";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
loadEnv();
|
||||
|
||||
try {
|
||||
validateApiKey(request);
|
||||
} catch (e) {
|
||||
return NextResponse.json({}, { status: 401, statusText: 'Unauthorized' });
|
||||
}
|
||||
|
||||
const analysisOrders = await getAnalysisOrdersAdmin({ orderStatus: 'QUEUED' });
|
||||
|
||||
console.error(`Sending test responses for ${analysisOrders.length} analysis orders`);
|
||||
for (const medreportOrder of analysisOrders) {
|
||||
const medusaOrderId = medreportOrder.medusa_order_id;
|
||||
const medusaOrder = await retrieveOrder(medusaOrderId)
|
||||
|
||||
const account = await getAccountAdmin({ primaryOwnerUserId: medreportOrder.user_id });
|
||||
const orderedAnalysisElementsIds = await getOrderedAnalysisElementsIds({ medusaOrder });
|
||||
|
||||
console.info(`Sending test response for order=${medusaOrderId} with ${orderedAnalysisElementsIds.length} ordered analysis elements`);
|
||||
const idsToSend = orderedAnalysisElementsIds;
|
||||
const messageXml = await composeOrderTestResponseXML({
|
||||
person: {
|
||||
idCode: account.personal_code!,
|
||||
firstName: account.name ?? '',
|
||||
lastName: account.last_name ?? '',
|
||||
phone: account.phone ?? '',
|
||||
},
|
||||
orderedAnalysisElementsIds: idsToSend.map(({ analysisElementId }) => analysisElementId),
|
||||
orderedAnalysesIds: [],
|
||||
orderId: medusaOrderId,
|
||||
orderCreatedAt: new Date(medreportOrder.created_at),
|
||||
});
|
||||
|
||||
console.info("SEND XML", messageXml);
|
||||
|
||||
try {
|
||||
await sendPrivateMessageTestResponse({ messageXml });
|
||||
} catch (error) {
|
||||
console.error("Error sending private message test response: ", error);
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { sendOrderToMedipost } from "~/lib/services/medipost.service";
|
||||
|
||||
export const POST = async (request: NextRequest) => {
|
||||
const { medusaOrderId } = (await request.json()) as { medusaOrderId: string };
|
||||
await sendOrderToMedipost({ medusaOrderId });
|
||||
return NextResponse.json({ success: true });
|
||||
};
|
||||
@@ -11,7 +11,7 @@ export async function POST(request: Request) {
|
||||
// return NextResponse.json({ error: 'This endpoint is only available in development mode' }, { status: 403 });
|
||||
// }
|
||||
|
||||
const { medusaOrderId } = await request.json();
|
||||
const { medusaOrderId, maxItems = null } = await request.json();
|
||||
|
||||
const medusaOrder = await retrieveOrder(medusaOrderId)
|
||||
const medreportOrder = await getOrder({ medusaOrderId });
|
||||
@@ -19,7 +19,8 @@ export async function POST(request: Request) {
|
||||
const account = await getAccountAdmin({ primaryOwnerUserId: medreportOrder.user_id });
|
||||
const orderedAnalysisElementsIds = await getOrderedAnalysisElementsIds({ medusaOrder });
|
||||
|
||||
console.info(`Sending test response for order=${medusaOrderId} with ${orderedAnalysisElementsIds.length} ordered analysis elements`);
|
||||
console.info(`Sending test response for order=${medusaOrderId} with ${orderedAnalysisElementsIds.length} (${maxItems ?? 'all'}) ordered analysis elements`);
|
||||
const idsToSend = typeof maxItems === 'number' ? orderedAnalysisElementsIds.slice(0, maxItems) : orderedAnalysisElementsIds;
|
||||
const messageXml = await composeOrderTestResponseXML({
|
||||
person: {
|
||||
idCode: account.personal_code!,
|
||||
@@ -27,7 +28,7 @@ export async function POST(request: Request) {
|
||||
lastName: account.last_name ?? '',
|
||||
phone: account.phone ?? '',
|
||||
},
|
||||
orderedAnalysisElementsIds: orderedAnalysisElementsIds.map(({ analysisElementId }) => analysisElementId),
|
||||
orderedAnalysisElementsIds: idsToSend.map(({ analysisElementId }) => analysisElementId),
|
||||
orderedAnalysesIds: [],
|
||||
orderId: medusaOrderId,
|
||||
orderCreatedAt: new Date(medreportOrder.created_at),
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { ArrowDown } from 'lucide-react';
|
||||
|
||||
import { cn } from '@kit/ui/utils';
|
||||
import { UserAnalysisElement } from '@/packages/features/accounts/src/types/accounts';
|
||||
|
||||
export enum AnalysisResultLevel {
|
||||
VERY_LOW = 0,
|
||||
@@ -17,11 +18,13 @@ const Level = ({
|
||||
color,
|
||||
isFirst = false,
|
||||
isLast = false,
|
||||
arrowLocation,
|
||||
}: {
|
||||
isActive?: boolean;
|
||||
color: 'destructive' | 'success' | 'warning' | 'gray-200';
|
||||
isFirst?: boolean;
|
||||
isLast?: boolean;
|
||||
arrowLocation?: number;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
@@ -32,7 +35,10 @@ const Level = ({
|
||||
})}
|
||||
>
|
||||
{isActive && (
|
||||
<div className="absolute top-[-14px] left-1/2 -translate-x-1/2 rounded-[10px] bg-white p-[2px]">
|
||||
<div
|
||||
className="absolute top-[-14px] left-1/2 -translate-x-1/2 rounded-[10px] bg-white p-[2px]"
|
||||
style={{ left: `${arrowLocation}%` }}
|
||||
>
|
||||
<ArrowDown strokeWidth={2} />
|
||||
</div>
|
||||
)}
|
||||
@@ -52,11 +58,33 @@ const AnalysisLevelBar = ({
|
||||
normLowerIncluded = true,
|
||||
normUpperIncluded = true,
|
||||
level,
|
||||
results,
|
||||
}: {
|
||||
normLowerIncluded?: boolean;
|
||||
normUpperIncluded?: boolean;
|
||||
level: AnalysisResultLevel;
|
||||
results: UserAnalysisElement;
|
||||
}) => {
|
||||
|
||||
const { norm_lower: lower, norm_upper: upper, response_value: value } = results;
|
||||
const arrowLocation = useMemo(() => {
|
||||
if (value < lower!) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (normLowerIncluded || normUpperIncluded) {
|
||||
return 50;
|
||||
}
|
||||
|
||||
const calculated = ((value - lower!) / (upper! - lower!)) * 100;
|
||||
|
||||
if (calculated > 100) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
return calculated;
|
||||
}, [value, upper, lower]);
|
||||
|
||||
return (
|
||||
<div className="mt-4 flex h-3 w-[35%] max-w-[360px] gap-1 sm:mt-0">
|
||||
{normLowerIncluded && (
|
||||
@@ -73,8 +101,9 @@ const AnalysisLevelBar = ({
|
||||
<Level
|
||||
isFirst={!normLowerIncluded}
|
||||
isLast={!normUpperIncluded}
|
||||
isActive={level === AnalysisResultLevel.NORMAL}
|
||||
color="success"
|
||||
color={level === AnalysisResultLevel.NORMAL ? "success" : "warning"}
|
||||
isActive
|
||||
arrowLocation={arrowLocation}
|
||||
/>
|
||||
|
||||
{normUpperIncluded && (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import { UserAnalysisElement } from '@/packages/features/accounts/src/types/accounts';
|
||||
import { format } from 'date-fns';
|
||||
@@ -39,11 +39,12 @@ const Analysis = ({
|
||||
const normUpper = results?.norm_upper || 0;
|
||||
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
const isUnderNorm = value < normLower;
|
||||
const getAnalysisResultLevel = () => {
|
||||
const analysisResultLevel = useMemo(() => {
|
||||
if (!results) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isUnderNorm = value < normLower;
|
||||
if (isUnderNorm) {
|
||||
switch (status) {
|
||||
case AnalysisStatus.MEDIUM:
|
||||
@@ -60,7 +61,7 @@ const Analysis = ({
|
||||
default:
|
||||
return AnalysisResultLevel.NORMAL;
|
||||
}
|
||||
};
|
||||
}, [results, value, normLower]);
|
||||
|
||||
return (
|
||||
<div className="border-border flex flex-col items-center justify-between gap-2 rounded-lg border px-5 px-12 py-3 sm:h-[65px] sm:flex-row sm:gap-0">
|
||||
@@ -99,9 +100,10 @@ const Analysis = ({
|
||||
</div>
|
||||
</div>
|
||||
<AnalysisLevelBar
|
||||
results={results}
|
||||
normLowerIncluded={normLowerIncluded}
|
||||
normUpperIncluded={normUpperIncluded}
|
||||
level={getAnalysisResultLevel()!}
|
||||
level={analysisResultLevel!}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -15,10 +15,11 @@ import {
|
||||
PAGE_VIEW_ACTION,
|
||||
createPageViewLog,
|
||||
} from '~/lib/services/audit/pageView.service';
|
||||
import { getAnalysisOrders } from '~/lib/services/order.service';
|
||||
import { AnalysisOrder, getAnalysisOrders } from '~/lib/services/order.service';
|
||||
import { ButtonTooltip } from '~/components/ui/button-tooltip';
|
||||
|
||||
import { loadUserAnalysis } from '../../_lib/server/load-user-analysis';
|
||||
import Analysis from './_components/analysis';
|
||||
import { loadUserAnalysis } from '../../_lib/server/load-user-analysis';
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
const i18n = await createI18nServerInstance();
|
||||
@@ -51,43 +52,16 @@ async function AnalysisResultsPage() {
|
||||
action: PAGE_VIEW_ACTION.VIEW_ANALYSIS_RESULTS,
|
||||
});
|
||||
|
||||
const analysisElementIds = [
|
||||
...new Set(
|
||||
analysisOrders
|
||||
?.flatMap((order) => order.analysis_element_ids)
|
||||
.filter(Boolean) as number[],
|
||||
),
|
||||
const getAnalysisElementIds = (analysisOrders: AnalysisOrder[]) => [
|
||||
...new Set(analysisOrders?.flatMap((order) => order.analysis_element_ids).filter(Boolean) as number[]),
|
||||
];
|
||||
const analysisElements = await getAnalysisElements({
|
||||
ids: analysisElementIds,
|
||||
});
|
||||
const analysisElementsWithResults =
|
||||
analysisResponseElements
|
||||
?.sort((a, b) => {
|
||||
if (!a.response_time || !b.response_time) {
|
||||
return 0;
|
||||
}
|
||||
return (
|
||||
new Date(b.response_time).getTime() -
|
||||
new Date(a.response_time).getTime()
|
||||
);
|
||||
})
|
||||
.map((results) => ({ results })) ?? [];
|
||||
const analysisElementsWithoutResults = analysisElements.filter(
|
||||
(element) =>
|
||||
!analysisElementsWithResults?.some(
|
||||
({ results }) =>
|
||||
results.analysis_element_original_id === element.analysis_id_original,
|
||||
),
|
||||
);
|
||||
|
||||
const hasNoAnalysisElements =
|
||||
analysisElementsWithResults.length === 0 &&
|
||||
analysisElementsWithoutResults.length === 0;
|
||||
const analysisElementIds = getAnalysisElementIds(analysisOrders);
|
||||
const analysisElements = await getAnalysisElements({ ids: analysisElementIds });
|
||||
|
||||
return (
|
||||
<PageBody>
|
||||
<div className="mt-8 flex flex-col justify-between gap-4 sm:flex-row sm:items-center sm:gap-0">
|
||||
<PageBody className="gap-4">
|
||||
<div className="mt-8 flex flex-col sm:flex-row sm:items-center justify-between gap-4 sm:gap-0">
|
||||
<div>
|
||||
<h4>
|
||||
<Trans i18nKey="analysis-results:pageTitle" />
|
||||
@@ -106,33 +80,46 @@ async function AnalysisResultsPage() {
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
{analysisElementsWithResults.map(({ results }) => {
|
||||
const analysisElement = analysisElements.find(
|
||||
(element) =>
|
||||
element.analysis_id_original ===
|
||||
results.analysis_element_original_id,
|
||||
);
|
||||
if (!analysisElement) {
|
||||
return null;
|
||||
}
|
||||
<div className="flex flex-col gap-8">
|
||||
{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 (
|
||||
<Analysis
|
||||
key={results.id}
|
||||
analysisElement={analysisElement}
|
||||
results={results}
|
||||
/>
|
||||
<div key={analysisOrder.id} className="flex flex-col gap-4">
|
||||
<h4>
|
||||
<Trans i18nKey="analysis-results:orderTitle" values={{ orderNumber: analysisOrder.medusa_order_id }} />
|
||||
</h4>
|
||||
<h5>
|
||||
<Trans i18nKey={`orders:status.${analysisOrder.status}`} />
|
||||
<ButtonTooltip
|
||||
content={`${new Date(analysisOrder.created_at).toLocaleString()}`}
|
||||
className="ml-6"
|
||||
/>
|
||||
</h5>
|
||||
<div className="flex flex-col gap-2">
|
||||
{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 (
|
||||
<Analysis key={`${analysisOrder.id}-${analysisElement.id}`} analysisElement={analysisElement} />
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Analysis key={`${analysisOrder.id}-${analysisElement.id}`} analysisElement={analysisElement} results={results} />
|
||||
);
|
||||
}) : (
|
||||
<div className="text-muted-foreground text-sm">
|
||||
<Trans i18nKey="analysis-results:noAnalysisElements" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{analysisElementsWithoutResults.map((element) => (
|
||||
<Analysis
|
||||
key={element.analysis_id_original}
|
||||
analysisElement={element}
|
||||
/>
|
||||
))}
|
||||
{hasNoAnalysisElements && (
|
||||
}) : (
|
||||
<div className="text-muted-foreground text-sm">
|
||||
<Trans i18nKey="analysis-results:noAnalysisElements" />
|
||||
<Trans i18nKey="analysis-results:noAnalysisOrders" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 (
|
||||
<PageBody>
|
||||
<PageHeader title={<Trans i18nKey="cart:title" />}>
|
||||
{item && item.updated_at && <CartTimer cartItem={item} />}
|
||||
{hasItemsWithTimer && item && item.updated_at && <CartTimer cartItem={item} />}
|
||||
</PageHeader>
|
||||
<Cart cart={cart} analysisPackages={analysisPackages} otherItems={otherItems} />
|
||||
</PageBody>
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
<HomeLayoutPageHeader
|
||||
|
||||
@@ -7,10 +7,11 @@ import { PageBody } from '@kit/ui/makerkit/page';
|
||||
import pathsConfig from '~/config/paths.config';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import { HomeLayoutPageHeader } from '../../_components/home-page-header';
|
||||
import OrdersTable from '../../_components/orders/orders-table';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
import type { IOrderLineItem } from '../../_components/orders/types';
|
||||
import { getAnalysisOrders } from '~/lib/services/order.service';
|
||||
import OrderBlock from '../../_components/orders/order-block';
|
||||
import React from 'react';
|
||||
import { Divider } from '@medusajs/ui';
|
||||
|
||||
export async function generateMetadata() {
|
||||
const { t } = await createI18nServerInstance();
|
||||
@@ -29,42 +30,7 @@ async function OrdersPage() {
|
||||
redirect(pathsConfig.auth.signIn);
|
||||
}
|
||||
|
||||
const analysisPackagesType = productTypes.find(({ metadata }) => 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={<Trans i18nKey={'orders:description'} />}
|
||||
/>
|
||||
<PageBody>
|
||||
<OrdersTable orderItems={analysisPackageOrders} title="orders:table.analysisPackage" />
|
||||
<OrdersTable orderItems={otherOrders} title="orders:table.otherOrders" />
|
||||
{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 (
|
||||
<React.Fragment key={analysisOrder.id}>
|
||||
<Divider className="my-6" />
|
||||
<OrderBlock
|
||||
analysisOrder={analysisOrder}
|
||||
itemsAnalysisPackage={medusaOrderItemsAnalysisPackages}
|
||||
itemsOther={medusaOrderItemsOther}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
})}
|
||||
</PageBody>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
<DashboardCards />
|
||||
<PageHeader title={
|
||||
<>
|
||||
<Trans i18nKey={'common:welcome'} />
|
||||
|
||||
@@ -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, {
|
||||
|
||||
45
app/home/(user)/_components/dashboard-cards.tsx
Normal file
45
app/home/(user)/_components/dashboard-cards.tsx
Normal file
@@ -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 (
|
||||
<div className='flex gap-4 lg:px-4'>
|
||||
<Card
|
||||
variant="gradient-success"
|
||||
className="flex flex-col justify-between"
|
||||
>
|
||||
<CardHeader className="flex-row">
|
||||
<div
|
||||
className={'flex size-8 items-center-safe justify-center-safe rounded-full text-white bg-primary\/10 mb-6'}
|
||||
>
|
||||
<HeartPulse className="size-4 fill-green-500" />
|
||||
</div>
|
||||
<div className='ml-auto flex size-8 items-center-safe justify-center-safe rounded-full text-white bg-warning'>
|
||||
<Link href='/home/order-analysis'>
|
||||
<Button size="icon" variant="outline" className="px-2 text-black">
|
||||
<ChevronRight className="size-4 stroke-2" />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardFooter className="flex flex-col items-start gap-2">
|
||||
<h5>
|
||||
<Trans i18nKey='dashboard:heroCard.orderAnalysis.title' />
|
||||
</h5>
|
||||
<CardDescription className="text-primary">
|
||||
<Trans i18nKey='dashboard:heroCard.orderAnalysis.description' />
|
||||
</CardDescription>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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({
|
||||
<div className="grid grid-cols-3 gap-6 mt-4">
|
||||
{analyses.map(({
|
||||
title,
|
||||
variants
|
||||
}) => (
|
||||
<Card
|
||||
key={title}
|
||||
variant="gradient-success"
|
||||
className="flex flex-col justify-between"
|
||||
>
|
||||
<CardHeader className="items-end-safe">
|
||||
<div className='flex size-8 items-center-safe justify-center-safe rounded-full text-white bg-warning'>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
className="px-2 text-black"
|
||||
onClick={() => handleSelect(variants![0]!)}
|
||||
variants,
|
||||
description,
|
||||
subtitle,
|
||||
status,
|
||||
metadata,
|
||||
}) => {
|
||||
const isAvailable = status === 'published' && !!metadata?.analysisIdOriginal;
|
||||
return (
|
||||
<Card
|
||||
key={title}
|
||||
variant={isAvailable ? "gradient-success" : "gradient-warning"}
|
||||
className="flex flex-col justify-between"
|
||||
>
|
||||
<CardHeader className="flex-row">
|
||||
<div
|
||||
className={'flex size-8 items-center-safe justify-center-safe rounded-full text-white bg-primary\/10 mb-6'}
|
||||
>
|
||||
{isAddingToCart ? <Loader2 className="size-4 stroke-2 animate-spin" /> : <ShoppingCart className="size-4 stroke-2" />}
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardFooter className="flex flex-col items-start gap-2">
|
||||
<div
|
||||
className={'flex size-8 items-center-safe justify-center-safe rounded-full text-white bg-primary\/10 mb-6'}
|
||||
>
|
||||
<HeartPulse className="size-4 fill-green-500" />
|
||||
</div>
|
||||
<h5>
|
||||
{title}
|
||||
</h5>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
))}
|
||||
<HeartPulse className="size-4 fill-green-500" />
|
||||
</div>
|
||||
{isAvailable && (
|
||||
<div className='ml-auto flex size-8 items-center-safe justify-center-safe rounded-full text-white bg-warning'>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
className="px-2 text-black"
|
||||
onClick={() => handleSelect(variants![0]!)}
|
||||
>
|
||||
{isAddingToCart ? <Loader2 className="size-4 stroke-2 animate-spin" /> : <ShoppingCart className="size-4 stroke-2" />}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</CardHeader>
|
||||
<CardFooter className="flex flex-col items-start gap-2">
|
||||
<h5>
|
||||
{title}
|
||||
{description && (
|
||||
<>
|
||||
{' '}
|
||||
<InfoTooltip content={`${description}`} />
|
||||
</>
|
||||
)}
|
||||
</h5>
|
||||
{isAvailable && subtitle && (
|
||||
<CardDescription>
|
||||
{subtitle}
|
||||
</CardDescription>
|
||||
)}
|
||||
{!isAvailable && (
|
||||
<CardDescription>
|
||||
<Trans i18nKey={'order-analysis:analysisNotAvailable'} />
|
||||
</CardDescription>
|
||||
)}
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
36
app/home/(user)/_components/orders/order-block.tsx
Normal file
36
app/home/(user)/_components/orders/order-block.tsx
Normal file
@@ -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 (
|
||||
<div className="flex flex-col gap-4">
|
||||
<h4>
|
||||
<Trans i18nKey="analysis-results:orderTitle" values={{ orderNumber: analysisOrder.medusa_order_id }} />
|
||||
</h4>
|
||||
<div className="flex gap-2">
|
||||
<h5>
|
||||
<Trans i18nKey={`orders:status.${analysisOrder.status}`} />
|
||||
</h5>
|
||||
<Link href={`/home/order/${analysisOrder.id}`} className="flex items-center justify-between text-small-regular">
|
||||
<button
|
||||
className="flex gap-x-1 text-ui-fg-subtle hover:text-ui-fg-base cursor-pointer"
|
||||
>
|
||||
<Eye />
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<OrderItemsTable items={itemsAnalysisPackage} title="orders:table.analysisPackage" analysisOrder={analysisOrder} />
|
||||
<OrderItemsTable items={itemsOther} title="orders:table.otherOrders" analysisOrder={analysisOrder} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
77
app/home/(user)/_components/orders/order-items-table.tsx
Normal file
77
app/home/(user)/_components/orders/order-items-table.tsx
Normal file
@@ -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 (
|
||||
<Table className="rounded-lg border border-separate">
|
||||
<TableHeader className="text-ui-fg-subtle txt-medium-plus">
|
||||
<TableRow>
|
||||
<TableHead className="px-6">
|
||||
<Trans i18nKey={title} />
|
||||
</TableHead>
|
||||
<TableHead className="px-6">
|
||||
<Trans i18nKey="orders:table.createdAt" />
|
||||
</TableHead>
|
||||
<TableHead className="px-6">
|
||||
<Trans i18nKey="orders:table.status" />
|
||||
</TableHead>
|
||||
<TableHead className="px-6">
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{items
|
||||
.sort((a, b) => (a.created_at ?? "") > (b.created_at ?? "") ? -1 : 1)
|
||||
.map((orderItem) => (
|
||||
<TableRow className="w-full" key={orderItem.id}>
|
||||
<TableCell className="text-left w-[100%] px-6">
|
||||
<p className="txt-medium-plus text-ui-fg-base">
|
||||
{orderItem.product_title}
|
||||
</p>
|
||||
</TableCell>
|
||||
|
||||
<TableCell className="px-6 whitespace-nowrap">
|
||||
{formatDate(orderItem.created_at, 'dd.MM.yyyy HH:mm')}
|
||||
</TableCell>
|
||||
|
||||
<TableCell className="px-6 min-w-[180px]">
|
||||
<Trans i18nKey={`orders:status.${analysisOrder.status}`} />
|
||||
</TableCell>
|
||||
|
||||
<TableCell className="text-right px-6">
|
||||
<span className="flex gap-x-1 justify-end w-[30px]">
|
||||
<Link href={`/home/analysis-results`} className="flex items-center justify-between text-small-regular">
|
||||
<button
|
||||
className="flex gap-x-1 text-ui-fg-subtle hover:text-ui-fg-base cursor-pointer"
|
||||
>
|
||||
<Eye />
|
||||
</button>
|
||||
</Link>
|
||||
</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
@@ -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 (
|
||||
<TableRow className="w-full">
|
||||
<TableCell className="text-left w-[100%] px-6">
|
||||
<p className="txt-medium-plus text-ui-fg-base">
|
||||
{orderItem.item.product_title}
|
||||
</p>
|
||||
</TableCell>
|
||||
|
||||
<TableCell className="px-6 whitespace-nowrap">
|
||||
{formatDate(orderItem.item.created_at, 'dd.MM.yyyy HH:mm')}
|
||||
</TableCell>
|
||||
|
||||
<TableCell className="px-6 whitespace-nowrap">
|
||||
<Trans i18nKey={`orders:status.${orderItem.orderStatus}`} />
|
||||
</TableCell>
|
||||
|
||||
<TableCell className="text-right px-6">
|
||||
<span className="flex gap-x-1 justify-end w-[60px]">
|
||||
<Link href={`/home/order/${orderItem.orderId}`} className="flex items-center justify-between text-small-regular">
|
||||
<button
|
||||
className="flex gap-x-1 text-ui-fg-subtle hover:text-ui-fg-base cursor-pointer"
|
||||
>
|
||||
<Eye />
|
||||
</button>
|
||||
</Link>
|
||||
</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
@@ -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 (
|
||||
<Table className="rounded-lg border border-separate">
|
||||
<TableHeader className="text-ui-fg-subtle txt-medium-plus">
|
||||
<TableRow>
|
||||
<TableHead className="px-6">
|
||||
<Trans i18nKey={title} />
|
||||
</TableHead>
|
||||
<TableHead className="px-6">
|
||||
<Trans i18nKey="orders:table.createdAt" />
|
||||
</TableHead>
|
||||
<TableHead className="px-6">
|
||||
<Trans i18nKey="orders:table.status" />
|
||||
</TableHead>
|
||||
<TableHead className="px-6">
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{orderItems
|
||||
.sort((a, b) => (a.item.created_at ?? "") > (b.item.created_at ?? "") ? -1 : 1)
|
||||
.map((orderItem) => (<OrdersItem key={orderItem.item.id} orderItem={orderItem} />))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { StoreOrderLineItem } from "@medusajs/types";
|
||||
|
||||
export interface IOrderLineItem {
|
||||
item: StoreOrderLineItem;
|
||||
medusaOrderId: string;
|
||||
orderId: number;
|
||||
orderStatus: string;
|
||||
}
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
<AppLogo />
|
||||
</div>
|
||||
|
||||
<div className={'flex items-center justify-end space-x-2.5 gap-2'}>
|
||||
<div className={'flex items-center justify-end gap-2 space-x-2.5'}>
|
||||
<TeamAccountNotifications accountId={account.id} userId={user.id} />
|
||||
<ProfileAccountDropdownContainer
|
||||
user={user}
|
||||
|
||||
@@ -45,11 +45,14 @@ function SidebarLayout({
|
||||
const data = use(loadTeamWorkspace(account));
|
||||
const state = use(getLayoutState(account));
|
||||
|
||||
const accounts = data.accounts.map(({ name, slug, picture_url }) => ({
|
||||
label: name,
|
||||
value: slug,
|
||||
image: picture_url,
|
||||
}));
|
||||
const accounts = data.accounts.map(
|
||||
({ name, slug, picture_url, application_role }) => ({
|
||||
label: name,
|
||||
value: slug,
|
||||
image: picture_url,
|
||||
application_role,
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<TeamAccountWorkspaceContextProvider value={data}>
|
||||
@@ -91,11 +94,14 @@ function HeaderLayout({
|
||||
}>) {
|
||||
const data = use(loadTeamWorkspace(account));
|
||||
|
||||
const accounts = data.accounts.map(({ name, slug, picture_url }) => ({
|
||||
label: name,
|
||||
value: slug,
|
||||
image: picture_url,
|
||||
}));
|
||||
const accounts = data.accounts.map(
|
||||
({ name, slug, picture_url, application_role }) => ({
|
||||
label: name,
|
||||
value: slug,
|
||||
image: picture_url,
|
||||
application_role,
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<TeamAccountWorkspaceContextProvider value={data}>
|
||||
|
||||
Reference in New Issue
Block a user