Merge branch 'develop' of https://github.com/MR-medreport/MRB2B into MED-103
This commit is contained in:
@@ -165,7 +165,7 @@ async function createProducts({
|
||||
medusa.admin.product.list({
|
||||
category_id: allCategories.map(({ id }) => id),
|
||||
}),
|
||||
getAnalysisElements({}),
|
||||
getAnalysisElements({ getAll: true }),
|
||||
getAnalysisPackagesType(),
|
||||
getProductDefaultFields({ medusa }),
|
||||
])
|
||||
|
||||
@@ -2,12 +2,12 @@ import axios from 'axios';
|
||||
import { XMLParser } from 'fast-xml-parser';
|
||||
import fs from 'fs';
|
||||
import { createAnalysisGroup, getAnalysisGroups } from '~/lib/services/analysis-group.service';
|
||||
import { IMedipostPublicMessageDataParsed } from '~/lib/services/medipost.types';
|
||||
import { createAnalysis, createNoDataReceivedEntry, createNoNewDataReceivedEntry, createSyncFailEntry, createSyncSuccessEntry } from '~/lib/services/analyses.service';
|
||||
import { IMedipostPublicMessageDataParsed } from '~/lib/services/medipost/medipost.types';
|
||||
import { createAnalysis, createNoDataReceivedEntry, createNoNewDataReceivedEntry, createSyncFailEntry, createSyncSuccessEntry, getAnalyses } from '~/lib/services/analyses.service';
|
||||
import { getLastCheckedDate } from '~/lib/services/sync-entries.service';
|
||||
import { createAnalysisElement } from '~/lib/services/analysis-element.service';
|
||||
import { AnalysisElement, createAnalysisElement, getAnalysisElements } from '~/lib/services/analysis-element.service';
|
||||
import { createCodes } from '~/lib/services/codes.service';
|
||||
import { getLatestPublicMessageListItem } from '~/lib/services/medipost.service';
|
||||
import { getLatestPublicMessageListItem } from '~/lib/services/medipost/medipostPublicMessage.service';
|
||||
import type { ICode } from '~/lib/types/code';
|
||||
|
||||
function toArray<T>(input?: T | T[] | null): T[] {
|
||||
@@ -82,42 +82,60 @@ export default async function syncAnalysisGroups() {
|
||||
const codes: ICode[] = [];
|
||||
for (const analysisGroup of analysisGroups) {
|
||||
const existingAnalysisGroup = existingAnalysisGroups?.find(({ original_id }) => original_id === analysisGroup.UuringuGruppId);
|
||||
let groupExistingAnalysisElements: AnalysisElement[] = [];
|
||||
let analysisGroupId: number;
|
||||
if (existingAnalysisGroup) {
|
||||
console.info(`Analysis group '${analysisGroup.UuringuGruppNimi}' already exists`);
|
||||
continue;
|
||||
console.info(`Analysis group '${analysisGroup.UuringuGruppNimi}' already exists, only creating new analysis elements`);
|
||||
groupExistingAnalysisElements = await getAnalysisElements({ analysisGroupId: existingAnalysisGroup.id });
|
||||
analysisGroupId = existingAnalysisGroup.id;
|
||||
} else {
|
||||
analysisGroupId = await createAnalysisGroup({
|
||||
id: analysisGroup.UuringuGruppId,
|
||||
name: analysisGroup.UuringuGruppNimi,
|
||||
order: analysisGroup.UuringuGruppJarjekord,
|
||||
});
|
||||
|
||||
const analysisGroupCodes = toArray(analysisGroup.Kood);
|
||||
codes.push(
|
||||
...analysisGroupCodes.map((kood) => ({
|
||||
hk_code: kood.HkKood,
|
||||
hk_code_multiplier: kood.HkKoodiKordaja,
|
||||
coefficient: kood.Koefitsient,
|
||||
price: kood.Hind,
|
||||
analysis_group_id: analysisGroupId,
|
||||
analysis_element_id: null,
|
||||
analysis_id: null,
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
// SAVE ANALYSIS GROUP
|
||||
const analysisGroupId = await createAnalysisGroup({
|
||||
id: analysisGroup.UuringuGruppId,
|
||||
name: analysisGroup.UuringuGruppNimi,
|
||||
order: analysisGroup.UuringuGruppJarjekord,
|
||||
});
|
||||
|
||||
const analysisGroupCodes = toArray(analysisGroup.Kood);
|
||||
codes.push(
|
||||
...analysisGroupCodes.map((kood) => ({
|
||||
hk_code: kood.HkKood,
|
||||
hk_code_multiplier: kood.HkKoodiKordaja,
|
||||
coefficient: kood.Koefitsient,
|
||||
price: kood.Hind,
|
||||
analysis_group_id: analysisGroupId,
|
||||
analysis_element_id: null,
|
||||
analysis_id: null,
|
||||
})),
|
||||
);
|
||||
|
||||
const analysisGroupItems = toArray(analysisGroup.Uuring);
|
||||
|
||||
for (const item of analysisGroupItems) {
|
||||
const analysisElement = item.UuringuElement;
|
||||
const analysisElement = item.UuringuElement!;
|
||||
const isExistingAnalysisElement = groupExistingAnalysisElements
|
||||
.find(({ analysis_id_original }) => analysis_id_original === analysisElement.UuringId);
|
||||
if (isExistingAnalysisElement) {
|
||||
console.info(`Analysis element '${analysisElement.UuringNimi}' already exists`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const insertedAnalysisElementId = await createAnalysisElement({
|
||||
analysisElement,
|
||||
analysisElement: analysisElement!,
|
||||
analysisGroupId,
|
||||
materialGroups: toArray(item.MaterjalideGrupp),
|
||||
});
|
||||
|
||||
if (Array.isArray(analysisElement.UuringuElement)) {
|
||||
for (const nestedAnalysisElement of analysisElement.UuringuElement) {
|
||||
await createAnalysisElement({
|
||||
analysisElement: nestedAnalysisElement,
|
||||
analysisGroupId,
|
||||
materialGroups: toArray(item.MaterjalideGrupp),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (analysisElement.Kood) {
|
||||
const analysisElementCodes = toArray(analysisElement.Kood);
|
||||
codes.push(
|
||||
@@ -135,7 +153,15 @@ export default async function syncAnalysisGroups() {
|
||||
|
||||
const analyses = analysisElement.UuringuElement;
|
||||
if (analyses?.length) {
|
||||
const existingAnalyses = await getAnalyses({ originalIds: analyses.map(({ UuringId }) => UuringId) });
|
||||
|
||||
for (const analysis of analyses) {
|
||||
const isExistingAnalysis = existingAnalyses.find(({ analysis_id_original }) => analysis_id_original === analysis.UuringId);
|
||||
if (isExistingAnalysis) {
|
||||
console.info(`Analysis '${analysis.UuringNimi}' already exists`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const insertedAnalysisId = await createAnalysis(analysis, analysisGroupId);
|
||||
|
||||
if (analysis.Kood) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { readPrivateMessageResponse } from "~/lib/services/medipost.service";
|
||||
import { readPrivateMessageResponse } from "~/lib/services/medipost/medipostPrivateMessage.service";
|
||||
|
||||
type ProcessedMessage = {
|
||||
messageId: string;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import loadEnv from "../handler/load-env";
|
||||
import validateApiKey from "../handler/validate-api-key";
|
||||
import { getOrderedAnalysisIds, sendOrderToMedipost } from "~/lib/services/medipost.service";
|
||||
import { getOrderedAnalysisIds } from "~/lib/services/medusaOrder.service";
|
||||
import { sendOrderToMedipost } from "~/lib/services/medipost/medipostPrivateMessage.service";
|
||||
import { retrieveOrder } from "@lib/data/orders";
|
||||
import { getMedipostDispatchTries } from "~/lib/services/audit.service";
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getAnalysisOrdersAdmin } from "~/lib/services/order.service";
|
||||
import { composeOrderTestResponseXML, sendPrivateMessageTestResponse } from "~/lib/services/medipostTest.service";
|
||||
import { composeOrderTestResponseXML, sendPrivateMessageTestResponse } from "~/lib/services/medipost/medipostTest.service";
|
||||
import { retrieveOrder } from "@lib/data";
|
||||
import { getAccountAdmin } from "~/lib/services/account.service";
|
||||
import { getOrderedAnalysisIds } from "~/lib/services/medipost.service";
|
||||
import { getOrderedAnalysisIds } from "~/lib/services/medusaOrder.service";
|
||||
import loadEnv from "../handler/load-env";
|
||||
import validateApiKey from "../handler/validate-api-key";
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getAnalysisOrder } from "~/lib/services/order.service";
|
||||
import { composeOrderTestResponseXML, sendPrivateMessageTestResponse } from "~/lib/services/medipostTest.service";
|
||||
import { composeOrderTestResponseXML, sendPrivateMessageTestResponse } from "~/lib/services/medipost/medipostTest.service";
|
||||
import { retrieveOrder } from "@lib/data";
|
||||
import { getAccountAdmin } from "~/lib/services/account.service";
|
||||
import { createMedipostActionLog, getOrderedAnalysisIds } from "~/lib/services/medipost.service";
|
||||
import { createMedipostActionLog } from "~/lib/services/medipost/medipostMessageBase.service";
|
||||
import { getOrderedAnalysisIds } from "~/lib/services/medusaOrder.service";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
// const isDev = process.env.NODE_ENV === 'development';
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { pathsConfig } from '@/packages/shared/src/config';
|
||||
import { ExternalLink } from '@/public/assets/external-link';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Button } from '@kit/ui/button';
|
||||
import { Checkbox } from '@kit/ui/checkbox';
|
||||
@@ -20,21 +21,22 @@ import {
|
||||
FormMessage,
|
||||
} from '@kit/ui/form';
|
||||
import { Input } from '@kit/ui/input';
|
||||
import { toast } from '@kit/ui/sonner';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { UpdateAccountSchemaClient } from '../_lib/schemas/update-account.schema';
|
||||
import { onUpdateAccount } from '../_lib/server/update-account';
|
||||
import { toast } from '@kit/ui/sonner';
|
||||
import { pathsConfig } from '@/packages/shared/src/config';
|
||||
|
||||
type UpdateAccountFormValues = z.infer<ReturnType<typeof UpdateAccountSchemaClient>>;
|
||||
type UpdateAccountFormValues = z.infer<
|
||||
ReturnType<typeof UpdateAccountSchemaClient>
|
||||
>;
|
||||
|
||||
export function UpdateAccountForm({
|
||||
defaultValues,
|
||||
isEmailUser,
|
||||
}: {
|
||||
defaultValues: UpdateAccountFormValues,
|
||||
isEmailUser: boolean,
|
||||
defaultValues: UpdateAccountFormValues;
|
||||
isEmailUser: boolean;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation('account');
|
||||
@@ -45,10 +47,13 @@ export function UpdateAccountForm({
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
const { firstName, lastName, personalCode, email, userConsent } = defaultValues;
|
||||
const { firstName, lastName, personalCode, email, userConsent } =
|
||||
defaultValues;
|
||||
|
||||
const defaultValues_weight = "weight" in defaultValues ? defaultValues.weight : null;
|
||||
const defaultValues_height = "height" in defaultValues ? defaultValues.height : null;
|
||||
const defaultValues_weight =
|
||||
'weight' in defaultValues ? defaultValues.weight : null;
|
||||
const defaultValues_height =
|
||||
'height' in defaultValues ? defaultValues.height : null;
|
||||
|
||||
const hasFirstName = !!firstName;
|
||||
const hasLastName = !!lastName;
|
||||
@@ -64,8 +69,12 @@ export function UpdateAccountForm({
|
||||
personalCode: values.personalCode || personalCode,
|
||||
email: values.email || email,
|
||||
phone: values.phone,
|
||||
weight: ((("weight" in values && values.weight) ?? defaultValues_weight) || null) as number,
|
||||
height: ((("height" in values && values.height) ?? defaultValues_height) || null) as number,
|
||||
weight: ((('weight' in values && values.weight) ??
|
||||
defaultValues_weight) ||
|
||||
null) as number,
|
||||
height: ((('height' in values && values.height) ??
|
||||
defaultValues_height) ||
|
||||
null) as number,
|
||||
userConsent: values.userConsent ?? userConsent,
|
||||
city: values.city,
|
||||
});
|
||||
@@ -81,7 +90,7 @@ export function UpdateAccountForm({
|
||||
router.push(pathsConfig.app.selectPackage);
|
||||
}
|
||||
} catch (error) {
|
||||
console.info("promiseresult error", error);
|
||||
console.info('promiseresult error', error);
|
||||
toast.error(t('updateAccount.updateAccountError'));
|
||||
toast.dismiss(loading);
|
||||
}
|
||||
@@ -193,7 +202,7 @@ export function UpdateAccountForm({
|
||||
<FormField
|
||||
name="weight"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormItem className="flex-1 basis-0">
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:weight'} />
|
||||
</FormLabel>
|
||||
@@ -205,7 +214,9 @@ export function UpdateAccountForm({
|
||||
value={field.value ?? ''}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target.value === '' ? null : Number(e.target.value),
|
||||
e.target.value === ''
|
||||
? null
|
||||
: Number(e.target.value),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -218,7 +229,7 @@ export function UpdateAccountForm({
|
||||
<FormField
|
||||
name="height"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormItem className="flex-1 basis-0">
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:height'} />
|
||||
</FormLabel>
|
||||
@@ -230,7 +241,9 @@ export function UpdateAccountForm({
|
||||
value={field.value ?? ''}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target.value === '' ? null : Number(e.target.value),
|
||||
e.target.value === ''
|
||||
? null
|
||||
: Number(e.target.value),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -260,7 +273,7 @@ export function UpdateAccountForm({
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href={''}
|
||||
href={t('account:updateAccount:userConsentUrlPath')}
|
||||
className="flex flex-row items-center gap-2 text-sm hover:underline"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
import { loadCurrentUserAccount } from '@/app/home/(user)/_lib/server/load-user-account';
|
||||
import { signOutAction } from '@/lib/actions/sign-out';
|
||||
|
||||
import { BackButton } from '@kit/shared/components/back-button';
|
||||
@@ -8,10 +9,9 @@ import { pathsConfig } from '@kit/shared/config';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
import { toTitleCase } from '~/lib/utils';
|
||||
|
||||
import { UpdateAccountForm } from './_components/update-account-form';
|
||||
import { loadCurrentUserAccount } from '@/app/home/(user)/_lib/server/load-user-account';
|
||||
import { toTitleCase } from '~/lib/utils';
|
||||
|
||||
async function UpdateAccount() {
|
||||
const { account, user } = await loadCurrentUserAccount();
|
||||
@@ -35,14 +35,14 @@ async function UpdateAccount() {
|
||||
})(),
|
||||
phone: account?.phone ?? '+372',
|
||||
city: account?.city ?? '',
|
||||
weight: account?.accountParams?.weight ?? 0,
|
||||
height: account?.accountParams?.height ?? 0,
|
||||
weight: account?.accountParams?.weight ?? null,
|
||||
height: account?.accountParams?.height ?? null,
|
||||
userConsent: account?.has_consent_personal_data ?? false,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="border-border flex max-w-5xl flex-row overflow-hidden rounded-3xl border">
|
||||
<div className="relative flex min-w-md flex-col px-12 pt-7 pb-22 text-center md:w-1/2">
|
||||
<div className="border-border xs:w-3/5 flex w-9/10 flex-row overflow-hidden rounded-3xl border md:w-[988px]">
|
||||
<div className="xs:px-6 relative flex w-full flex-col pt-7 pb-22 text-center md:w-1/2 md:px-12">
|
||||
<BackButton onBack={signOutAction} />
|
||||
<MedReportLogo />
|
||||
<h1 className="pt-8">
|
||||
@@ -51,9 +51,12 @@ async function UpdateAccount() {
|
||||
<p className="text-muted-foreground pt-1 text-sm">
|
||||
<Trans i18nKey={'account:updateAccount:description'} />
|
||||
</p>
|
||||
<UpdateAccountForm defaultValues={defaultValues} isEmailUser={isEmailUser} />
|
||||
<UpdateAccountForm
|
||||
defaultValues={defaultValues}
|
||||
isEmailUser={isEmailUser}
|
||||
/>
|
||||
</div>
|
||||
<div className="hidden w-1/2 min-w-[460px] bg-[url(/assets/med-report-logo-big.png)] bg-cover bg-center bg-no-repeat md:block"></div>
|
||||
<div className="hidden w-1/2 bg-[url(/assets/med-report-logo-big.png)] bg-cover bg-center bg-no-repeat md:block"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
149
app/doctor/_components/analysis-doctor.tsx
Normal file
149
app/doctor/_components/analysis-doctor.tsx
Normal file
@@ -0,0 +1,149 @@
|
||||
'use client';
|
||||
|
||||
import React, { ReactElement, ReactNode, useMemo, useState } from 'react';
|
||||
|
||||
import { UserAnalysisElement } from '@/packages/features/accounts/src/types/accounts';
|
||||
import { format } from 'date-fns';
|
||||
import { Info } from 'lucide-react';
|
||||
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import { cn } from '@kit/ui/utils';
|
||||
|
||||
import { AnalysisElement } from '~/lib/services/analysis-element.service';
|
||||
|
||||
import AnalysisLevelBar, {
|
||||
AnalysisLevelBarSkeleton,
|
||||
AnalysisResultLevel,
|
||||
} from './analysis-level-bar';
|
||||
|
||||
export type AnalysisResultForDisplay = Pick<
|
||||
UserAnalysisElement,
|
||||
| 'norm_status'
|
||||
| 'response_value'
|
||||
| 'unit'
|
||||
| 'norm_lower_included'
|
||||
| 'norm_upper_included'
|
||||
| 'norm_lower'
|
||||
| 'norm_upper'
|
||||
| 'response_time'
|
||||
>;
|
||||
|
||||
export enum AnalysisStatus {
|
||||
NORMAL = 0,
|
||||
MEDIUM = 1,
|
||||
HIGH = 2,
|
||||
}
|
||||
|
||||
const AnalysisDoctor = ({
|
||||
analysisElement,
|
||||
results,
|
||||
startIcon,
|
||||
endIcon,
|
||||
isCancelled,
|
||||
}: {
|
||||
analysisElement: Pick<AnalysisElement, 'analysis_name_lab'>;
|
||||
results?: AnalysisResultForDisplay;
|
||||
isCancelled?: boolean;
|
||||
startIcon?: ReactElement | null;
|
||||
endIcon?: ReactNode | null;
|
||||
}) => {
|
||||
const name = analysisElement.analysis_name_lab || '';
|
||||
const status = results?.norm_status || AnalysisStatus.NORMAL;
|
||||
const value = results?.response_value || 0;
|
||||
const unit = results?.unit || '';
|
||||
const normLowerIncluded = results?.norm_lower_included || false;
|
||||
const normUpperIncluded = results?.norm_upper_included || false;
|
||||
const normLower = results?.norm_lower || 0;
|
||||
const normUpper = results?.norm_upper || 0;
|
||||
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
const analysisResultLevel = useMemo(() => {
|
||||
if (!results) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isUnderNorm = value < normLower;
|
||||
if (isUnderNorm) {
|
||||
switch (status) {
|
||||
case AnalysisStatus.MEDIUM:
|
||||
return AnalysisResultLevel.LOW;
|
||||
default:
|
||||
return AnalysisResultLevel.VERY_LOW;
|
||||
}
|
||||
}
|
||||
switch (status) {
|
||||
case AnalysisStatus.MEDIUM:
|
||||
return AnalysisResultLevel.HIGH;
|
||||
case AnalysisStatus.HIGH:
|
||||
return AnalysisResultLevel.VERY_HIGH;
|
||||
default:
|
||||
return AnalysisResultLevel.NORMAL;
|
||||
}
|
||||
}, [results, value, normLower]);
|
||||
|
||||
return (
|
||||
<div className="border-border rounded-lg border px-5">
|
||||
<div className="flex flex-col items-center justify-between gap-2 py-3 sm:h-[65px] sm:flex-row sm:gap-0">
|
||||
<div className="flex items-center gap-2 font-semibold">
|
||||
{startIcon || <div className="w-4" />}
|
||||
{name}
|
||||
{results?.response_time && (
|
||||
<div
|
||||
className="group/tooltip relative"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setShowTooltip(!showTooltip);
|
||||
}}
|
||||
onMouseLeave={() => setShowTooltip(false)}
|
||||
>
|
||||
<Info className="hover" />{' '}
|
||||
<div
|
||||
className={cn(
|
||||
'absolute bottom-full left-1/2 z-10 mb-2 hidden -translate-x-1/2 rounded border bg-white p-4 text-sm whitespace-nowrap group-hover/tooltip:block',
|
||||
{ block: showTooltip },
|
||||
)}
|
||||
>
|
||||
<Trans i18nKey="analysis-results:analysisDate" />
|
||||
{': '}
|
||||
{format(new Date(results.response_time), 'dd.MM.yyyy HH:mm')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{results ? (
|
||||
<>
|
||||
<div className="flex items-center gap-3 sm:ml-auto">
|
||||
<div className="font-semibold">{value}</div>
|
||||
<div className="text-muted-foreground text-sm">{unit}</div>
|
||||
</div>
|
||||
<div className="text-muted-foreground mx-8 flex flex-col-reverse gap-2 text-center text-sm sm:block sm:gap-0">
|
||||
{normLower} - {normUpper}
|
||||
<div>
|
||||
<Trans i18nKey="analysis-results:results.range.normal" />
|
||||
</div>
|
||||
</div>
|
||||
<AnalysisLevelBar
|
||||
results={results}
|
||||
normLowerIncluded={normLowerIncluded}
|
||||
normUpperIncluded={normUpperIncluded}
|
||||
level={analysisResultLevel!}
|
||||
/>
|
||||
{endIcon || <div className="mx-2 w-4" />}
|
||||
</>
|
||||
) : (isCancelled ? null : (
|
||||
<>
|
||||
<div className="flex items-center gap-3 sm:ml-auto">
|
||||
<div className="font-semibold">
|
||||
<Trans i18nKey="analysis-results:waitingForResults" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-8 w-[60px]"></div>
|
||||
<AnalysisLevelBarSkeleton />
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnalysisDoctor;
|
||||
134
app/doctor/_components/analysis-level-bar.tsx
Normal file
134
app/doctor/_components/analysis-level-bar.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { ArrowDown } from 'lucide-react';
|
||||
|
||||
import { cn } from '@kit/ui/utils';
|
||||
import { AnalysisResultForDisplay } from './analysis-doctor';
|
||||
|
||||
export enum AnalysisResultLevel {
|
||||
VERY_LOW = 0,
|
||||
LOW = 1,
|
||||
NORMAL = 2,
|
||||
HIGH = 3,
|
||||
VERY_HIGH = 4,
|
||||
}
|
||||
|
||||
const Level = ({
|
||||
isActive = false,
|
||||
color,
|
||||
isFirst = false,
|
||||
isLast = false,
|
||||
arrowLocation,
|
||||
}: {
|
||||
isActive?: boolean;
|
||||
color: 'destructive' | 'success' | 'warning' | 'gray-200';
|
||||
isFirst?: boolean;
|
||||
isLast?: boolean;
|
||||
arrowLocation?: number;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(`bg-${color} relative h-3 flex-1`, {
|
||||
'opacity-20': !isActive,
|
||||
'rounded-l-lg': isFirst,
|
||||
'rounded-r-lg': isLast,
|
||||
})}
|
||||
>
|
||||
{isActive && (
|
||||
<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>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const AnalysisLevelBarSkeleton = () => {
|
||||
return (
|
||||
<div className="mt-4 flex h-3 w-[60%] sm:w-[35%] max-w-[360px] gap-1 sm:mt-0">
|
||||
<Level color="gray-200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AnalysisLevelBar = ({
|
||||
normLowerIncluded = true,
|
||||
normUpperIncluded = true,
|
||||
level,
|
||||
results,
|
||||
}: {
|
||||
normLowerIncluded?: boolean;
|
||||
normUpperIncluded?: boolean;
|
||||
level: AnalysisResultLevel;
|
||||
results: AnalysisResultForDisplay;
|
||||
}) => {
|
||||
|
||||
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]);
|
||||
|
||||
const [isVeryLow, isLow, isHigh, isVeryHigh] = useMemo(() => [
|
||||
level === AnalysisResultLevel.VERY_LOW,
|
||||
level === AnalysisResultLevel.LOW,
|
||||
level === AnalysisResultLevel.HIGH,
|
||||
level === AnalysisResultLevel.VERY_HIGH,
|
||||
], [level, value, upper, lower]);
|
||||
|
||||
const hasAbnormalLevel = isVeryLow || isLow || isHigh || isVeryHigh;
|
||||
|
||||
return (
|
||||
<div className="mt-4 flex h-3 w-[60%] sm:w-[35%] max-w-[360px] gap-1 sm:mt-0">
|
||||
{normLowerIncluded && (
|
||||
<>
|
||||
<Level
|
||||
isActive={isVeryLow}
|
||||
color="destructive"
|
||||
isFirst
|
||||
/>
|
||||
<Level isActive={isLow} color="warning" />
|
||||
</>
|
||||
)}
|
||||
|
||||
<Level
|
||||
isFirst={!normLowerIncluded}
|
||||
isLast={!normUpperIncluded}
|
||||
{...(hasAbnormalLevel ? { color: "warning", isActive: false } : { color: "success", isActive: true })}
|
||||
arrowLocation={arrowLocation}
|
||||
/>
|
||||
|
||||
{normUpperIncluded && (
|
||||
<>
|
||||
<Level
|
||||
isActive={isHigh}
|
||||
color="warning"
|
||||
/>
|
||||
<Level
|
||||
isActive={isVeryHigh}
|
||||
color="destructive"
|
||||
isLast
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnalysisLevelBar;
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
} from '@kit/ui/collapsible';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import Analysis from '~/home/(user)/(dashboard)/analysis-results/_components/analysis';
|
||||
import AnalysisDoctor from './analysis-doctor';
|
||||
|
||||
export default function DoctorAnalysisWrapper({
|
||||
analysisData,
|
||||
@@ -29,7 +29,7 @@ export default function DoctorAnalysisWrapper({
|
||||
asChild
|
||||
>
|
||||
<div className="[&[data-state=open]_.caret-icon]:rotate-180">
|
||||
<Analysis
|
||||
<AnalysisDoctor
|
||||
startIcon={
|
||||
analysisData.latestPreviousAnalysis && (
|
||||
<CaretDownIcon className="caret-icon transition-transform duration-200" />
|
||||
@@ -65,7 +65,7 @@ export default function DoctorAnalysisWrapper({
|
||||
{analysisData.latestPreviousAnalysis && (
|
||||
<CollapsibleContent>
|
||||
<div className="my-1 flex flex-col">
|
||||
<Analysis
|
||||
<AnalysisDoctor
|
||||
endIcon={
|
||||
analysisData.latestPreviousAnalysis.comment && (
|
||||
<>
|
||||
|
||||
@@ -52,6 +52,8 @@ export default async function AnalysisResultsPage({
|
||||
);
|
||||
}
|
||||
|
||||
const orderedAnalysisElements = analysisResponse.orderedAnalysisElements;
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader />
|
||||
@@ -80,7 +82,7 @@ export default async function AnalysisResultsPage({
|
||||
<h4>
|
||||
<Trans
|
||||
i18nKey="analysis-results:orderTitle"
|
||||
values={{ orderNumber: analysisResponse.order.medusa_order_id }}
|
||||
values={{ orderNumber: analysisResponse.order.medusaOrderId }}
|
||||
/>
|
||||
</h4>
|
||||
<h5>
|
||||
@@ -88,7 +90,7 @@ export default async function AnalysisResultsPage({
|
||||
i18nKey={`orders:status.${analysisResponse.order.status}`}
|
||||
/>
|
||||
<ButtonTooltip
|
||||
content={`${analysisResponse.order.created_at ? new Date(analysisResponse?.order?.created_at).toLocaleString() : ''}`}
|
||||
content={`${analysisResponse.order.createdAt ? new Date(analysisResponse?.order?.createdAt).toLocaleString() : ''}`}
|
||||
className="ml-6"
|
||||
/>
|
||||
</h5>
|
||||
@@ -102,13 +104,9 @@ export default async function AnalysisResultsPage({
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col gap-2">
|
||||
{analysisResponse.elements ? (
|
||||
analysisResponse.elements.map((element, index) => (
|
||||
<Analysis
|
||||
key={index}
|
||||
analysisElement={{ analysis_name_lab: element.analysis_name }}
|
||||
results={element}
|
||||
/>
|
||||
{orderedAnalysisElements ? (
|
||||
orderedAnalysisElements.map((element, index) => (
|
||||
<Analysis key={index} element={element} />
|
||||
))
|
||||
) : (
|
||||
<div className="text-muted-foreground text-sm">
|
||||
|
||||
@@ -3,14 +3,12 @@ import { useMemo } from 'react';
|
||||
import { ArrowDown } from 'lucide-react';
|
||||
|
||||
import { cn } from '@kit/ui/utils';
|
||||
import { AnalysisResultForDisplay } from './analysis';
|
||||
import { AnalysisResultDetailsElementResults } from '@/packages/features/accounts/src/types/analysis-results';
|
||||
|
||||
export enum AnalysisResultLevel {
|
||||
VERY_LOW = 0,
|
||||
LOW = 1,
|
||||
NORMAL = 2,
|
||||
HIGH = 3,
|
||||
VERY_HIGH = 4,
|
||||
NORMAL = 0,
|
||||
WARNING = 1,
|
||||
CRITICAL = 2,
|
||||
}
|
||||
|
||||
const Level = ({
|
||||
@@ -19,17 +17,19 @@ const Level = ({
|
||||
isFirst = false,
|
||||
isLast = false,
|
||||
arrowLocation,
|
||||
normRangeText,
|
||||
}: {
|
||||
isActive?: boolean;
|
||||
color: 'destructive' | 'success' | 'warning' | 'gray-200';
|
||||
isFirst?: boolean;
|
||||
isLast?: boolean;
|
||||
arrowLocation?: number;
|
||||
normRangeText?: string | null;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(`bg-${color} relative h-3 flex-1`, {
|
||||
'opacity-20': !isActive,
|
||||
'opacity-60': !isActive,
|
||||
'rounded-l-lg': isFirst,
|
||||
'rounded-r-lg': isLast,
|
||||
})}
|
||||
@@ -37,96 +37,176 @@ const Level = ({
|
||||
{isActive && (
|
||||
<div
|
||||
className="absolute top-[-14px] left-1/2 -translate-x-1/2 rounded-[10px] bg-white p-[2px]"
|
||||
style={{ left: `${arrowLocation}%` }}
|
||||
{...(arrowLocation ? {
|
||||
style: {
|
||||
left: `${arrowLocation}%`,
|
||||
...(arrowLocation > 92.5 && { left: '92.5%' }),
|
||||
...(arrowLocation < 7.5 && { left: '7.5%' }),
|
||||
}
|
||||
} : {})}
|
||||
>
|
||||
<ArrowDown strokeWidth={2} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{color === 'success' && typeof normRangeText === 'string' && (
|
||||
<p className={cn("absolute bottom-[-18px] left-3/8 text-xs text-muted-foreground font-bold", {
|
||||
'opacity-60': isActive,
|
||||
})}>
|
||||
{normRangeText}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const AnalysisLevelBarSkeleton = () => {
|
||||
return (
|
||||
<div className="mt-4 flex h-3 w-[35%] max-w-[360px] gap-1 sm:mt-0">
|
||||
<div className="mt-4 flex h-3 w-[60%] sm:w-[35%] max-w-[360px] gap-1 sm:mt-0">
|
||||
<Level color="gray-200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AnalysisLevelBar = ({
|
||||
normLowerIncluded = true,
|
||||
normUpperIncluded = true,
|
||||
level,
|
||||
results,
|
||||
normRangeText,
|
||||
}: {
|
||||
normLowerIncluded?: boolean;
|
||||
normUpperIncluded?: boolean;
|
||||
level: AnalysisResultLevel;
|
||||
results: AnalysisResultForDisplay;
|
||||
results: AnalysisResultDetailsElementResults;
|
||||
normRangeText: string | null;
|
||||
}) => {
|
||||
|
||||
const { norm_lower: lower, norm_upper: upper, response_value: value } = results;
|
||||
const { normLower: lower, normUpper: upper, responseValue: value, normStatus } = results;
|
||||
const normLowerIncluded = results?.normLowerIncluded || false;
|
||||
const normUpperIncluded = results?.normUpperIncluded || false;
|
||||
|
||||
// Calculate arrow position based on value within normal range
|
||||
const arrowLocation = useMemo(() => {
|
||||
if (value < lower!) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (normLowerIncluded || normUpperIncluded) {
|
||||
// If no response value, center the arrow
|
||||
if (value === null || value === undefined) {
|
||||
return 50;
|
||||
}
|
||||
|
||||
const calculated = ((value - lower!) / (upper! - lower!)) * 100;
|
||||
|
||||
if (calculated > 100) {
|
||||
return 100;
|
||||
// If no normal ranges defined, center the arrow
|
||||
if (lower === null && upper === null) {
|
||||
return 50;
|
||||
}
|
||||
|
||||
return calculated;
|
||||
|
||||
// If only upper bound exists
|
||||
if (lower === null && upper !== null) {
|
||||
if (value <= upper) {
|
||||
return Math.min(75, (value / upper) * 75); // Show in left 75% of normal range
|
||||
}
|
||||
return 100; // Beyond upper bound
|
||||
}
|
||||
|
||||
// If only lower bound exists
|
||||
if (upper === null && lower !== null) {
|
||||
if (value >= lower) {
|
||||
// Value is in normal range (above lower bound)
|
||||
// Position proportionally in the normal range section
|
||||
const normalizedPosition = Math.min((value - lower) / (lower * 0.5), 1); // Use 50% of lower as scale
|
||||
return normalizedPosition * 100;
|
||||
}
|
||||
// Value is below lower bound - position in the "below normal" section
|
||||
const belowPosition = Math.max(0, Math.min(1, value / lower));
|
||||
return belowPosition * 100;
|
||||
}
|
||||
|
||||
// Both bounds exist
|
||||
if (lower !== null && upper !== null) {
|
||||
if (value < lower) {
|
||||
return 0; // Below normal range
|
||||
}
|
||||
if (value > upper) {
|
||||
return 100; // Above normal range
|
||||
}
|
||||
// Within normal range
|
||||
return ((value - lower) / (upper - lower)) * 100;
|
||||
}
|
||||
|
||||
return 50; // Fallback
|
||||
}, [value, upper, lower]);
|
||||
|
||||
const [isVeryLow, isLow, isHigh, isVeryHigh] = useMemo(() => [
|
||||
level === AnalysisResultLevel.VERY_LOW,
|
||||
level === AnalysisResultLevel.LOW,
|
||||
level === AnalysisResultLevel.HIGH,
|
||||
level === AnalysisResultLevel.VERY_HIGH,
|
||||
], [level, value, upper, lower]);
|
||||
// Determine level states based on normStatus
|
||||
const isNormal = level === AnalysisResultLevel.NORMAL;
|
||||
const isWarning = level === AnalysisResultLevel.WARNING;
|
||||
const isCritical = level === AnalysisResultLevel.CRITICAL;
|
||||
const isPending = level === null;
|
||||
|
||||
const hasAbnormalLevel = isVeryLow || isLow || isHigh || isVeryHigh;
|
||||
// If pending results, show gray bar
|
||||
if (isPending) {
|
||||
return (
|
||||
<div className="mt-4 flex h-3 w-60% sm:w-[35%] max-w-[360px] gap-1 sm:mt-0">
|
||||
<Level color="gray-200" isFirst isLast />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Show appropriate levels based on available norm bounds
|
||||
const hasLowerBound = lower !== null;
|
||||
const isLowerBoundZero = hasLowerBound && lower === 0;
|
||||
console.info('isLowerBoundZero', results.analysisElementOriginalId, { isLowerBoundZero, hasLowerBound, lower });
|
||||
const hasUpperBound = upper !== null;
|
||||
|
||||
// Determine which section the value falls into
|
||||
const isValueBelowLower = hasLowerBound && value !== null && value < lower!;
|
||||
const isValueAboveUpper = hasUpperBound && value !== null && value > upper!;
|
||||
const isValueInNormalRange = !isValueBelowLower && !isValueAboveUpper;
|
||||
|
||||
const [first, second, third] = useMemo(() => {
|
||||
if (!hasLowerBound) {
|
||||
return [
|
||||
{
|
||||
isActive: isNormal,
|
||||
color: "success",
|
||||
isFirst: true,
|
||||
normRangeText,
|
||||
...(isNormal ? { arrowLocation } : {}),
|
||||
},
|
||||
{
|
||||
isActive: isWarning,
|
||||
color: "warning",
|
||||
...(isWarning ? { arrowLocation } : {}),
|
||||
},
|
||||
{
|
||||
isActive: isCritical,
|
||||
color: "destructive",
|
||||
isLast: true,
|
||||
...(isCritical ? { arrowLocation } : {}),
|
||||
},
|
||||
] as const;
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
isActive: isWarning,
|
||||
color: "warning",
|
||||
isFirst: true,
|
||||
...(isWarning ? { arrowLocation } : {}),
|
||||
},
|
||||
{
|
||||
isActive: isNormal,
|
||||
color: "success",
|
||||
normRangeText,
|
||||
...(isNormal ? { arrowLocation } : {}),
|
||||
},
|
||||
{
|
||||
isActive: isCritical,
|
||||
color: "destructive",
|
||||
isLast: true,
|
||||
...(isCritical ? { arrowLocation } : {}),
|
||||
},
|
||||
] as const;
|
||||
}, [isValueBelowLower, isValueAboveUpper, isValueInNormalRange, arrowLocation, normRangeText, isNormal, isWarning, isCritical]);
|
||||
|
||||
return (
|
||||
<div className="mt-4 flex h-3 w-[35%] max-w-[360px] gap-1 sm:mt-0">
|
||||
{normLowerIncluded && (
|
||||
<>
|
||||
<Level
|
||||
isActive={isVeryLow}
|
||||
color="destructive"
|
||||
isFirst
|
||||
/>
|
||||
<Level isActive={isLow} color="warning" />
|
||||
</>
|
||||
)}
|
||||
|
||||
<Level
|
||||
isFirst={!normLowerIncluded}
|
||||
isLast={!normUpperIncluded}
|
||||
{...(hasAbnormalLevel ? { color: "warning", isActive: false } : { color: "success", isActive: true })}
|
||||
arrowLocation={arrowLocation}
|
||||
/>
|
||||
|
||||
{normUpperIncluded && (
|
||||
<>
|
||||
<Level
|
||||
isActive={isHigh}
|
||||
color="warning"
|
||||
/>
|
||||
<Level
|
||||
isActive={isVeryHigh}
|
||||
color="destructive"
|
||||
isLast
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<div className="mt-4 flex h-3 w-[60%] sm:w-[35%] max-w-[360px] gap-1 sm:mt-0">
|
||||
<Level {...first} />
|
||||
<Level {...second} />
|
||||
<Level {...third} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,33 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import React, { ReactElement, ReactNode, useMemo, useState } from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { UserAnalysisElement } from '@/packages/features/accounts/src/types/accounts';
|
||||
import { AnalysisResultDetailsElement } from '@/packages/features/accounts/src/types/analysis-results';
|
||||
import { format } from 'date-fns';
|
||||
import { Info } from 'lucide-react';
|
||||
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import { cn } from '@kit/ui/utils';
|
||||
|
||||
import { AnalysisElement } from '~/lib/services/analysis-element.service';
|
||||
|
||||
import AnalysisLevelBar, {
|
||||
AnalysisLevelBarSkeleton,
|
||||
AnalysisResultLevel,
|
||||
} from './analysis-level-bar';
|
||||
|
||||
export type AnalysisResultForDisplay = Pick<
|
||||
UserAnalysisElement,
|
||||
| 'norm_status'
|
||||
| 'response_value'
|
||||
| 'unit'
|
||||
| 'norm_lower_included'
|
||||
| 'norm_upper_included'
|
||||
| 'norm_lower'
|
||||
| 'norm_upper'
|
||||
| 'response_time'
|
||||
>;
|
||||
|
||||
export enum AnalysisStatus {
|
||||
NORMAL = 0,
|
||||
MEDIUM = 1,
|
||||
@@ -35,26 +21,45 @@ export enum AnalysisStatus {
|
||||
}
|
||||
|
||||
const Analysis = ({
|
||||
analysisElement,
|
||||
results,
|
||||
startIcon,
|
||||
endIcon,
|
||||
isCancelled,
|
||||
element,
|
||||
}: {
|
||||
analysisElement: Pick<AnalysisElement, 'analysis_name_lab'>;
|
||||
results?: AnalysisResultForDisplay;
|
||||
isCancelled?: boolean;
|
||||
startIcon?: ReactElement | null;
|
||||
endIcon?: ReactNode | null;
|
||||
element: AnalysisResultDetailsElement;
|
||||
}) => {
|
||||
const name = analysisElement.analysis_name_lab || '';
|
||||
const status = results?.norm_status || AnalysisStatus.NORMAL;
|
||||
const value = results?.response_value || 0;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const name = element.analysisName || '';
|
||||
const results = element.results;
|
||||
|
||||
const hasIsWithinNorm = results?.responseValueIsWithinNorm !== null;
|
||||
const hasIsNegative = results?.responseValueIsNegative !== null;
|
||||
|
||||
const value = (() => {
|
||||
if (!results) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { responseValue, responseValueIsNegative, responseValueIsWithinNorm } = results;
|
||||
if (responseValue === null || responseValue === undefined) {
|
||||
if (hasIsNegative) {
|
||||
if (responseValueIsNegative) {
|
||||
return t('analysis-results:results.value.negative');
|
||||
}
|
||||
return t('analysis-results:results.value.positive');
|
||||
}
|
||||
if (hasIsWithinNorm) {
|
||||
if (responseValueIsWithinNorm) {
|
||||
return t('analysis-results:results.value.isWithinNorm');
|
||||
}
|
||||
return t('analysis-results:results.value.isNotWithinNorm');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return responseValue;
|
||||
})();
|
||||
const unit = results?.unit || '';
|
||||
const normLowerIncluded = results?.norm_lower_included || false;
|
||||
const normUpperIncluded = results?.norm_upper_included || false;
|
||||
const normLower = results?.norm_lower || 0;
|
||||
const normUpper = results?.norm_upper || 0;
|
||||
const normLower = results?.normLower;
|
||||
const normUpper = results?.normUpper;
|
||||
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
const analysisResultLevel = useMemo(() => {
|
||||
@@ -62,32 +67,34 @@ const Analysis = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const isUnderNorm = value < normLower;
|
||||
if (isUnderNorm) {
|
||||
switch (status) {
|
||||
case AnalysisStatus.MEDIUM:
|
||||
return AnalysisResultLevel.LOW;
|
||||
default:
|
||||
return AnalysisResultLevel.VERY_LOW;
|
||||
}
|
||||
if (results.responseValue === null || results.responseValue === undefined) {
|
||||
return null;
|
||||
}
|
||||
switch (status) {
|
||||
case AnalysisStatus.MEDIUM:
|
||||
return AnalysisResultLevel.HIGH;
|
||||
case AnalysisStatus.HIGH:
|
||||
return AnalysisResultLevel.VERY_HIGH;
|
||||
|
||||
const normStatus = results.normStatus;
|
||||
|
||||
switch (normStatus) {
|
||||
case 1:
|
||||
return AnalysisResultLevel.WARNING;
|
||||
case 2:
|
||||
return AnalysisResultLevel.CRITICAL;
|
||||
case 0:
|
||||
default:
|
||||
return AnalysisResultLevel.NORMAL;
|
||||
}
|
||||
}, [results, value, normLower]);
|
||||
}, [results]);
|
||||
|
||||
const isCancelled = Number(results?.status) === 5;
|
||||
const hasNestedElements = results?.nestedElements.length > 0;
|
||||
|
||||
const normRangeText = normLower !== null ? `${normLower} - ${normUpper || ''}` : null;
|
||||
|
||||
return (
|
||||
<div className="border-border rounded-lg border px-5">
|
||||
<div className="flex flex-col items-center justify-between gap-2 py-3 sm:h-[65px] sm:flex-row sm:gap-0">
|
||||
<div className="flex flex-col items-center justify-between gap-2 pt-3 pb-6 sm:py-3 sm:h-[65px] sm:flex-row sm:gap-0">
|
||||
<div className="flex items-center gap-2 font-semibold">
|
||||
{startIcon || <div className="w-4" />}
|
||||
{name}
|
||||
{results?.response_time && (
|
||||
{results?.responseTime && (
|
||||
<div
|
||||
className="group/tooltip relative"
|
||||
onClick={(e) => {
|
||||
@@ -105,42 +112,41 @@ const Analysis = ({
|
||||
>
|
||||
<Trans i18nKey="analysis-results:analysisDate" />
|
||||
{': '}
|
||||
{format(new Date(results.response_time), 'dd.MM.yyyy HH:mm')}
|
||||
{format(new Date(results.responseTime), 'dd.MM.yyyy HH:mm')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{results ? (
|
||||
|
||||
{isCancelled && (
|
||||
<div className="text-red-600 font-semibold text-sm">
|
||||
<Trans i18nKey="analysis-results:cancelled" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isCancelled || !results || hasNestedElements ? null : (
|
||||
<>
|
||||
<div className="flex items-center gap-3 sm:ml-auto">
|
||||
<div className="font-semibold">{value}</div>
|
||||
<div className="text-muted-foreground text-sm">{unit}</div>
|
||||
</div>
|
||||
<div className="text-muted-foreground mx-8 flex flex-col-reverse gap-2 text-center text-sm sm:block sm:gap-0">
|
||||
{normLower} - {normUpper}
|
||||
<div>
|
||||
<Trans i18nKey="analysis-results:results.range.normal" />
|
||||
</div>
|
||||
</div>
|
||||
<AnalysisLevelBar
|
||||
results={results}
|
||||
normLowerIncluded={normLowerIncluded}
|
||||
normUpperIncluded={normUpperIncluded}
|
||||
level={analysisResultLevel!}
|
||||
/>
|
||||
{endIcon || <div className="mx-2 w-4" />}
|
||||
{!(hasIsNegative || hasIsWithinNorm) && (
|
||||
<>
|
||||
<div className="text-muted-foreground mx-8 flex flex-col-reverse gap-2 text-center text-sm sm:block sm:gap-0">
|
||||
{normRangeText}
|
||||
<div>
|
||||
<Trans i18nKey="analysis-results:results.range.normal" />
|
||||
</div>
|
||||
</div>
|
||||
<AnalysisLevelBar
|
||||
results={results}
|
||||
level={analysisResultLevel!}
|
||||
normRangeText={normRangeText}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (isCancelled ? null : (
|
||||
<>
|
||||
<div className="flex items-center gap-3 sm:ml-auto">
|
||||
<div className="font-semibold">
|
||||
<Trans i18nKey="analysis-results:waitingForResults" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-8 w-[60px]"></div>
|
||||
<AnalysisLevelBarSkeleton />
|
||||
</>
|
||||
))}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
99
app/home/(user)/(dashboard)/analysis-results/test/page.tsx
Normal file
99
app/home/(user)/(dashboard)/analysis-results/test/page.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { PageBody, PageHeader } from '@kit/ui/page';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import { Button } from '@kit/ui/shadcn/button';
|
||||
import Modal from "@modules/common/components/modal"
|
||||
|
||||
import Analysis from '../_components/analysis';
|
||||
import { analysisResponses } from './test-responses';
|
||||
|
||||
export default function AnalysisResultsPage() {
|
||||
const [openBlocks, setOpenBlocks] = useState<number[]>([]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader />
|
||||
<PageBody className="gap-4">
|
||||
<div className="mt-8 flex flex-col justify-between gap-4 sm:flex-row sm:items-center sm:gap-0">
|
||||
<div>
|
||||
<h2>
|
||||
Analüüsi tulemused demo
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
{analysisResponses.map(({ id, orderedAnalysisElements }, index) => {
|
||||
const isOpen = openBlocks.includes(id);
|
||||
const closeModal = () => setOpenBlocks(openBlocks.filter((block) => block !== id));
|
||||
return (
|
||||
<div key={index} className="flex flex-col gap-2 py-4">
|
||||
<div className="flex flex-col gap-2 pb-4">
|
||||
<h3>AnalysisOrderId: {id}</h3>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<h5>OrderedAnalysisElements</h5>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (isOpen) {
|
||||
closeModal();
|
||||
} else {
|
||||
setOpenBlocks([...openBlocks, id]);
|
||||
}
|
||||
}}
|
||||
className="w-fit"
|
||||
color={isOpen ? 'orange' : 'grey'}
|
||||
>
|
||||
{isOpen ? 'Close' : 'Open'}
|
||||
</Button>
|
||||
{isOpen && (
|
||||
<Modal isOpen={isOpen} close={closeModal} size="large">
|
||||
<div className="overflow-y-auto">
|
||||
|
||||
<p>NormiStaatus</p>
|
||||
<ul>
|
||||
<li>0 - testi väärtus jääb normaalväärtuste piirkonda või on määramata,</li>
|
||||
<li>1 - testi väärtus jääb hoiatava (tähelepanu suunava) märkega piirkonda,</li>
|
||||
<li>2 - testi väärtus on normaalväärtuste piirkonnast väljas või kõrgendatud tähelepanu nõudvas piirkonnas.</li>
|
||||
</ul>
|
||||
|
||||
<p>UuringOlek</p>
|
||||
<ul>
|
||||
<li>1 - Järjekorras,</li>
|
||||
<li>2 - Ootel,</li>
|
||||
<li>3 - Töös,</li>
|
||||
<li>4 - Lõpetatud,</li>
|
||||
<li>5 - Tagasi lükatud,</li>
|
||||
<li>6 - Tühistatud,</li>
|
||||
</ul>
|
||||
|
||||
<pre className="text-sm bg-muted p-4 rounded-md">
|
||||
{JSON.stringify(orderedAnalysisElements, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
</Modal>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{orderedAnalysisElements ? (
|
||||
orderedAnalysisElements.map((element, index) => (
|
||||
<Analysis key={index} element={element} />
|
||||
))
|
||||
) : (
|
||||
<div className="text-muted-foreground text-sm">
|
||||
<Trans i18nKey="analysis-results:noAnalysisElements" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</PageBody>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,840 @@
|
||||
import { AnalysisResultDetailsMapped } from "@/packages/features/accounts/src/types/analysis-results";
|
||||
|
||||
type AnalysisTestResponse = Omit<AnalysisResultDetailsMapped, 'order' | 'orderedAnalysisElementIds' | 'summary' | 'elements'>;
|
||||
|
||||
const empty1: AnalysisTestResponse = {
|
||||
"id": 1,
|
||||
"orderedAnalysisElements": [],
|
||||
};
|
||||
|
||||
const big1: AnalysisTestResponse = {
|
||||
"id": 2,
|
||||
"orderedAnalysisElements": [
|
||||
{
|
||||
"analysisIdOriginal": "1744-2",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "ALAT",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": "U/l",
|
||||
"normLower": null,
|
||||
"normUpper": 45,
|
||||
"normStatus": 2,
|
||||
"responseTime": "2024-02-29T10:42:25+00:00",
|
||||
"responseValue": 84,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "1744-2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "1920-8",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "ASAT",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": "U/l",
|
||||
"normLower": 15,
|
||||
"normUpper": 45,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:20:55+00:00",
|
||||
"responseValue": 45,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "1920-8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "1988-5",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "CRP",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": "mg/l",
|
||||
"normLower": null,
|
||||
"normUpper": 5,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:18:49+00:00",
|
||||
"responseValue": 0.79,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "1988-5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "57747-8",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Erütrotsüüdid",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": null,
|
||||
"normLower": null,
|
||||
"normUpper": 5,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:13:01+00:00",
|
||||
"responseValue": null,
|
||||
"responseValueIsNegative": true,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "57747-8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "2276-4",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Ferritiin",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": "µg/l",
|
||||
"normLower": 28,
|
||||
"normUpper": 370,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:46:54+00:00",
|
||||
"responseValue": 204.1,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "2276-4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "14771-0",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Glükoos",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": "mmol/l",
|
||||
"normLower": 4.1,
|
||||
"normUpper": 6,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:06:24+00:00",
|
||||
"responseValue": 5.4,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "14771-0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "59156-0",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Glükoos",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": null,
|
||||
"normLower": null,
|
||||
"normUpper": 2,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:13:01+00:00",
|
||||
"responseValue": null,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": false,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "59156-0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "13955-0",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "HCV Ab",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": null,
|
||||
"normLower": null,
|
||||
"normUpper": null,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T13:44:48+00:00",
|
||||
"responseValue": null,
|
||||
"responseValueIsNegative": true,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "13955-0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "14646-4",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "HDL kolesterool",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": "mmol/l",
|
||||
"normLower": 1,
|
||||
"normUpper": null,
|
||||
"normStatus": 1,
|
||||
"responseTime": "2024-02-29T10:20:55+00:00",
|
||||
"responseValue": 0.8,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "14646-4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "2000-8",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Kaltsium",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": "mmol/l",
|
||||
"normLower": 2.1,
|
||||
"normUpper": 2.55,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:12:10+00:00",
|
||||
"responseValue": 2.49,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "2000-8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "59158-6",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Ketokehad",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": null,
|
||||
"normLower": null,
|
||||
"normUpper": 0.5,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:13:01+00:00",
|
||||
"responseValue": null,
|
||||
"responseValueIsNegative": true,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "59158-6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "14647-2",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Kolesterool",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": "mmol/l",
|
||||
"normLower": null,
|
||||
"normUpper": 5,
|
||||
"normStatus": 1,
|
||||
"responseTime": "2024-02-29T10:20:34+00:00",
|
||||
"responseValue": 5.7,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "14647-2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "14682-9",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Kreatiniin",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": "µmol/l",
|
||||
"normLower": 64,
|
||||
"normUpper": 111,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:19:00+00:00",
|
||||
"responseValue": 89,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "14682-9"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "22748-8",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "LDL kolesterool",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": "mmol/l",
|
||||
"normLower": null,
|
||||
"normUpper": 3,
|
||||
"normStatus": 1,
|
||||
"responseTime": "2024-02-29T10:21:15+00:00",
|
||||
"responseValue": 4.3,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "22748-8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "58805-3",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Leukotsüüdid",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": null,
|
||||
"normLower": null,
|
||||
"normUpper": 10,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:13:01+00:00",
|
||||
"responseValue": null,
|
||||
"responseValueIsNegative": true,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "58805-3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "2601-3",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Magneesium",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": "mmol/l",
|
||||
"normLower": 0.66,
|
||||
"normUpper": 1.07,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:17:26+00:00",
|
||||
"responseValue": 0.82,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "2601-3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "70204-3",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Mitte-HDL kolesterool",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"labComment": "Mitte-paastu veri <3,9 mmol/L",
|
||||
"unit": "mmol/l",
|
||||
"normLower": null,
|
||||
"normUpper": 3.8,
|
||||
"normStatus": 1,
|
||||
"responseTime": "2024-02-29T10:20:55+00:00",
|
||||
"responseValue": 4.9,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "70204-3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "14798-3",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Raud",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": "µmol/l",
|
||||
"normLower": 11.6,
|
||||
"normUpper": 31.3,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:21:16+00:00",
|
||||
"responseValue": 16.5,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "14798-3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "14927-8",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Triglütseriidid",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"labComment": "Mitte-paastu veri <2,0 mmol/L",
|
||||
"unit": "mmol/l",
|
||||
"normLower": null,
|
||||
"normUpper": 1.7,
|
||||
"normStatus": 1,
|
||||
"responseTime": "2024-02-29T10:21:16+00:00",
|
||||
"responseValue": 1.89,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "14927-8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "3016-3",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "TSH",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": "mIU/l",
|
||||
"normLower": 0.4,
|
||||
"normUpper": 4,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:49:02+00:00",
|
||||
"responseValue": 1.27,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "3016-3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "22664-7",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Uurea",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": "mmol/l",
|
||||
"normLower": 3.2,
|
||||
"normUpper": 7.4,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:19:11+00:00",
|
||||
"responseValue": 6.4,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "22664-7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "50561-0",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Valk",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": null,
|
||||
"normLower": null,
|
||||
"normUpper": 0.25,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:13:01+00:00",
|
||||
"responseValue": null,
|
||||
"responseValueIsNegative": true,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "50561-0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "60493-4",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Vitamiin D (25-OH)",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"labComment": "Väärtus >75 nmol/l on D-vitamiini tervislik tase",
|
||||
"unit": "nmol/l",
|
||||
"normLower": 75,
|
||||
"normUpper": null,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:49:22+00:00",
|
||||
"responseValue": 105.5,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "60493-4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "60025-4",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Urobilinogeen",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": null,
|
||||
"normLower": null,
|
||||
"normUpper": 17,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2024-02-29T10:13:01+00:00",
|
||||
"responseValue": null,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": true,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "60025-4"
|
||||
},
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
const big2: AnalysisTestResponse = {
|
||||
"id": 3,
|
||||
"orderedAnalysisElements": [
|
||||
{
|
||||
"analysisIdOriginal": "1988-5",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "CRP",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"unit": "mg/L",
|
||||
"normLower": null,
|
||||
"normUpper": 5,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12T14:02:04+00:00",
|
||||
"responseValue": 1,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "1988-5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "57021-8",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Hemogramm",
|
||||
"results": {
|
||||
"nestedElements": [
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "g/L",
|
||||
"normLower": 134,
|
||||
"normUpper": 170,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:03",
|
||||
"responseValue": 150,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "718-7"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "%",
|
||||
"normLower": 40,
|
||||
"normUpper": 49,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:03",
|
||||
"responseValue": 45,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "4544-3"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 4.1,
|
||||
"normUpper": 9.7,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:03",
|
||||
"responseValue": 5,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "6690-2"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E12/L",
|
||||
"normLower": 4.5,
|
||||
"normUpper": 5.7,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:03",
|
||||
"responseValue": 5,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "789-8"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "fL",
|
||||
"normLower": 82,
|
||||
"normUpper": 95,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 85,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "787-2"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "pg",
|
||||
"normLower": 28,
|
||||
"normUpper": 33,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 30,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "785-6"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "g/L",
|
||||
"normLower": 322,
|
||||
"normUpper": 356,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 355,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "786-4"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "%",
|
||||
"normLower": 12,
|
||||
"normUpper": 15,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 15,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "788-0"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 157,
|
||||
"normUpper": 372,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 255,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "777-3"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "%",
|
||||
"normLower": 0.18,
|
||||
"normUpper": 0.38,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 0.2,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "51637-7"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "fL",
|
||||
"normLower": 9.2,
|
||||
"normUpper": 12.3,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 10,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "32623-1"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "fL",
|
||||
"normLower": 10.1,
|
||||
"normUpper": 16.2,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 15,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "32207-3"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 0.01,
|
||||
"normUpper": 0.08,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 0.05,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "704-7"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 0.02,
|
||||
"normUpper": 0.4,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 0.05,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "711-2"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 1.9,
|
||||
"normUpper": 6.7,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 5,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "751-8"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 0.24,
|
||||
"normUpper": 0.8,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 0.5,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "742-7"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 1.3,
|
||||
"normUpper": 3.1,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 1.5,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "731-0"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 0,
|
||||
"normUpper": 0.03,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:03",
|
||||
"responseValue": 0,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "51584-1"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "%",
|
||||
"normLower": 0,
|
||||
"normUpper": 0.5,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 0,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "38518-7"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 0,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "771-6"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "/100WBC",
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 0,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "58413-6"
|
||||
}
|
||||
],
|
||||
"unit": null,
|
||||
"normLower": null,
|
||||
"normUpper": null,
|
||||
"normStatus": null,
|
||||
"responseTime": null,
|
||||
"responseValue": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"responseValueIsNegative": false,
|
||||
"responseValueIsWithinNorm": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "57021-8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "43583-4",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Lipoproteiin a",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"labComment": "Kliendi soovil analüüs tühistatud.",
|
||||
"unit": null,
|
||||
"normLower": null,
|
||||
"normUpper": null,
|
||||
"normStatus": null,
|
||||
"responseTime": null,
|
||||
"responseValue": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"responseValueIsNegative": false,
|
||||
"responseValueIsWithinNorm": false,
|
||||
"status": "5",
|
||||
"analysisElementOriginalId": "43583-4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"analysisIdOriginal": "60493-4",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Vitamiin D (25-OH)",
|
||||
"results": {
|
||||
"nestedElements": [],
|
||||
"labComment": "Väärtus vahemikus 30-49.9 nmol/L on D-vitamiini ebapiisav tase.",
|
||||
"unit": "nmol/L",
|
||||
"normLower": 75,
|
||||
"normUpper": null,
|
||||
"normStatus": 1,
|
||||
"responseTime": "2025-09-12T14:02:04+00:00",
|
||||
"responseValue": 30,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"responseValueIsNegative": null,
|
||||
"responseValueIsWithinNorm": null,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "60493-4"
|
||||
}
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
export const analysisResponses: AnalysisTestResponse[] = [
|
||||
empty1,
|
||||
big1,
|
||||
big2,
|
||||
];
|
||||
@@ -7,30 +7,21 @@ import { listProductTypes } from '@lib/data/products';
|
||||
import type { StoreOrder } from '@medusajs/types';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { AccountWithParams } from '@kit/accounts/api';
|
||||
import { createNotificationsApi } from '@kit/notifications/api';
|
||||
import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client';
|
||||
|
||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||
import {
|
||||
bookAppointment,
|
||||
getOrderedTtoServices,
|
||||
} from '~/lib/services/connected-online.service';
|
||||
import {
|
||||
getOrderedAnalysisIds,
|
||||
sendOrderToMedipost,
|
||||
} from '~/lib/services/medipost.service';
|
||||
import {
|
||||
createAnalysisOrder,
|
||||
getAnalysisOrder,
|
||||
} from '~/lib/services/order.service';
|
||||
|
||||
import { FailureReason } from '../../../../../../lib/types/connected-online';
|
||||
import { FailureReason } from '~/lib/types/connected-online';
|
||||
import { createAnalysisOrder, getAnalysisOrder } from '~/lib/services/order.service';
|
||||
import { sendOrderToMedipost } from '~/lib/services/medipost/medipostPrivateMessage.service';
|
||||
import { getOrderedAnalysisIds } from '~/lib/services/medusaOrder.service';
|
||||
import { AccountWithParams } from '@kit/accounts/types/accounts';
|
||||
|
||||
const ANALYSIS_PACKAGES_TYPE_HANDLE = 'analysis-packages';
|
||||
const ANALYSIS_TYPE_HANDLE = 'synlab-analysis';
|
||||
const TTO_SERVICE_TYPE_HANDLE = 'tto-service';
|
||||
const MONTONIO_PAID_STATUS = 'PAID';
|
||||
|
||||
const env = () =>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
import { toTitleCase } from '@/lib/utils';
|
||||
import { createAccountsApi } from '@/packages/features/accounts/src/server/api';
|
||||
import { createUserAnalysesApi } from '@kit/user-analyses/api';
|
||||
import { getSupabaseServerClient } from '@/packages/supabase/src/clients/server-client';
|
||||
|
||||
import { PageBody, PageHeader } from '@kit/ui/page';
|
||||
@@ -27,7 +27,7 @@ async function UserHomePage() {
|
||||
const client = getSupabaseServerClient();
|
||||
|
||||
const { account } = await loadCurrentUserAccount();
|
||||
const api = createAccountsApi(client);
|
||||
const api = createUserAnalysesApi(client);
|
||||
const bmiThresholds = await api.fetchBmiThresholds();
|
||||
|
||||
if (!account) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import type { AccountWithParams } from '@/packages/features/accounts/src/server/api';
|
||||
import type { AccountWithParams } from '@kit/accounts/types/accounts';
|
||||
import { Database } from '@/packages/supabase/src/database.types';
|
||||
import { BlendingModeIcon, RulerHorizontalIcon } from '@radix-ui/react-icons';
|
||||
import {
|
||||
|
||||
@@ -4,12 +4,12 @@ import { useMemo } from 'react';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import SignOutDropdownItem from '@kit/shared/components/sign-out-dropdown-item';
|
||||
import { StoreCart } from '@medusajs/types';
|
||||
import { Cross, Menu, Shield, ShoppingCart } from 'lucide-react';
|
||||
import { Cross, Menu, Shield, ShoppingCart, UserCircle } from 'lucide-react';
|
||||
|
||||
import { usePersonalAccountData } from '@kit/accounts/hooks/use-personal-account-data';
|
||||
import { ApplicationRoleEnum } from '@kit/accounts/types/accounts';
|
||||
import SignOutDropdownItem from '@kit/shared/components/sign-out-dropdown-item';
|
||||
import DropdownLink from '@kit/shared/components/ui/dropdown-link';
|
||||
import {
|
||||
pathsConfig,
|
||||
@@ -102,7 +102,14 @@ export function HomeMobileNavigation(props: {
|
||||
<DropdownMenuSeparator />
|
||||
</If>
|
||||
|
||||
<DropdownMenuGroup>{Links}</DropdownMenuGroup>
|
||||
<DropdownMenuGroup>
|
||||
{Links}
|
||||
<DropdownLink
|
||||
Icon={<UserCircle className="w-4 stroke-[1.5px]" />}
|
||||
path={pathsConfig.app.personalAccountSettings}
|
||||
label="common:routes.profile"
|
||||
/>
|
||||
</DropdownMenuGroup>
|
||||
|
||||
<If condition={isSuperAdmin}>
|
||||
<DropdownMenuSeparator />
|
||||
@@ -146,5 +153,3 @@ export function HomeMobileNavigation(props: {
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import { formatDate } from 'date-fns';
|
||||
import { AnalysisOrder } from "~/lib/services/order.service";
|
||||
import type { AnalysisOrder } from "~/lib/types/analysis-order";
|
||||
|
||||
export default function OrderDetails({ order }: {
|
||||
order: AnalysisOrder
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AnalysisOrder } from "~/lib/services/order.service";
|
||||
import type { AnalysisOrder } from "~/lib/types/analysis-order";
|
||||
import { Trans } from '@kit/ui/makerkit/trans';
|
||||
import { StoreOrderLineItem } from "@medusajs/types";
|
||||
import OrderItemsTable from "./order-items-table";
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
} from '@kit/ui/table';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { AnalysisOrder } from '~/lib/services/order.service';
|
||||
import type { AnalysisOrder } from '~/lib/types/analysis-order';
|
||||
|
||||
import { logAnalysisResultsNavigateAction } from './actions';
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import { listRegions } from '@lib/data/regions';
|
||||
import { getAnalysisElementMedusaProductIds } from '@/utils/medusa-product';
|
||||
import type { StoreProduct } from '@medusajs/types';
|
||||
import { loadCurrentUserAccount } from './load-user-account';
|
||||
import { AccountWithParams } from '@/packages/features/accounts/src/server/api';
|
||||
import { AnalysisPackageWithVariant } from '@kit/shared/components/select-analysis-package';
|
||||
import type { AccountWithParams } from '@kit/accounts/types/accounts';
|
||||
import type { AnalysisPackageWithVariant } from '@kit/shared/components/select-analysis-package';
|
||||
import PersonalCode from '~/lib/utils';
|
||||
|
||||
async function countryCodesLoader() {
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import { cache } from 'react';
|
||||
|
||||
import { createAccountsApi } from '@kit/accounts/api';
|
||||
import { UserAnalysis } from '@kit/accounts/types/accounts';
|
||||
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||
|
||||
export type UserAnalyses = Awaited<ReturnType<typeof loadUserAnalyses>>;
|
||||
|
||||
/**
|
||||
* @name loadUserAnalyses
|
||||
* @description
|
||||
* Load the user's analyses. It's a cached per-request function that fetches the user workspace data.
|
||||
* It can be used across the server components to load the user workspace data.
|
||||
*/
|
||||
export const loadUserAnalyses = cache(analysesLoader);
|
||||
|
||||
async function analysesLoader(): Promise<UserAnalysis | null> {
|
||||
const client = getSupabaseServerClient();
|
||||
const api = createAccountsApi(client);
|
||||
|
||||
return api.getUserAnalyses();
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { cache } from 'react';
|
||||
|
||||
import { createAccountsApi } from '@kit/accounts/api';
|
||||
import { AnalysisResultDetails } from '@kit/accounts/types/accounts';
|
||||
import { AnalysisResultDetailsMapped } from '@kit/accounts/types/analysis-results';
|
||||
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||
import { createUserAnalysesApi } from '@/packages/features/user-analyses/src/server/api';
|
||||
|
||||
export type UserAnalyses = Awaited<ReturnType<typeof loadUserAnalysis>>;
|
||||
|
||||
@@ -15,9 +15,9 @@ export const loadUserAnalysis = cache(analysisLoader);
|
||||
|
||||
async function analysisLoader(
|
||||
analysisOrderId: number,
|
||||
): Promise<AnalysisResultDetails | null> {
|
||||
): Promise<AnalysisResultDetailsMapped | null> {
|
||||
const client = getSupabaseServerClient();
|
||||
const api = createAccountsApi(client);
|
||||
const api = createUserAnalysesApi(client);
|
||||
|
||||
return api.getUserAnalysis(analysisOrderId);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
import { AccountWithParams } from '@kit/accounts/api';
|
||||
import type { AccountWithParams } from '@kit/accounts/types/accounts';
|
||||
import { useRevalidatePersonalAccountDataQuery } from '@kit/accounts/hooks/use-personal-account-data';
|
||||
import { Button } from '@kit/ui/button';
|
||||
import { Card, CardDescription, CardTitle } from '@kit/ui/card';
|
||||
|
||||
@@ -4,7 +4,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
import { AccountWithParams } from '@kit/accounts/api';
|
||||
import type { AccountWithParams } from '@kit/accounts/types/accounts';
|
||||
import { useRevalidatePersonalAccountDataQuery } from '@kit/accounts/hooks/use-personal-account-data';
|
||||
import { Button } from '@kit/ui/button';
|
||||
import {
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
import { use } from 'react';
|
||||
|
||||
import { createAccountsApi } from '@/packages/features/accounts/src/server/api';
|
||||
import { CompanyGuard } from '@/packages/features/team-accounts/src/components';
|
||||
import { createTeamAccountsApi } from '@/packages/features/team-accounts/src/server/api';
|
||||
import { getSupabaseServerClient } from '@/packages/supabase/src/clients/server-client';
|
||||
import { createUserAnalysesApi } from '@kit/user-analyses/api';
|
||||
|
||||
import { PageBody } from '@kit/ui/page';
|
||||
|
||||
@@ -35,10 +35,10 @@ function TeamAccountHomePage({ params }: TeamAccountHomePageProps) {
|
||||
const account = use(params).account;
|
||||
const client = getSupabaseServerClient();
|
||||
const teamAccountsApi = createTeamAccountsApi(client);
|
||||
const accountsApi = createAccountsApi(client);
|
||||
const userAnalysesApi = createUserAnalysesApi(client);
|
||||
const teamAccount = use(teamAccountsApi.getTeamAccount(account));
|
||||
const { memberParams, members } = use(teamAccountsApi.getMembers(account));
|
||||
const bmiThresholds = use(accountsApi.fetchBmiThresholds());
|
||||
const bmiThresholds = use(userAnalysesApi.fetchBmiThresholds());
|
||||
const companyParams = use(
|
||||
teamAccountsApi.getTeamAccountParams(teamAccount.id),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user