feat(MED-105): update analysis results page
This commit is contained in:
@@ -19,7 +19,7 @@ const Level = ({
|
||||
isLast = false,
|
||||
}: {
|
||||
isActive?: boolean;
|
||||
color: 'destructive' | 'success' | 'warning';
|
||||
color: 'destructive' | 'success' | 'warning' | 'gray-200';
|
||||
isFirst?: boolean;
|
||||
isLast?: boolean;
|
||||
}) => {
|
||||
@@ -40,6 +40,14 @@ const Level = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const AnalysisLevelBarSkeleton = () => {
|
||||
return (
|
||||
<div className="mt-4 flex h-3 max-w-[360px] w-[35%] gap-1 sm:mt-0">
|
||||
<Level color="gray-200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AnalysisLevelBar = ({
|
||||
normLowerIncluded = true,
|
||||
normUpperIncluded = true,
|
||||
@@ -50,7 +58,7 @@ const AnalysisLevelBar = ({
|
||||
level: AnalysisResultLevel;
|
||||
}) => {
|
||||
return (
|
||||
<div className="mt-4 flex h-3 w-full max-w-[360px] gap-1 sm:mt-0">
|
||||
<div className="mt-4 flex h-3 max-w-[360px] w-[35%] gap-1 sm:mt-0">
|
||||
{normLowerIncluded && (
|
||||
<>
|
||||
<Level
|
||||
|
||||
@@ -6,7 +6,10 @@ import { Info } from 'lucide-react';
|
||||
|
||||
import { cn } from '@kit/ui/utils';
|
||||
|
||||
import AnalysisLevelBar, { AnalysisResultLevel } from './analysis-level-bar';
|
||||
import AnalysisLevelBar, { AnalysisLevelBarSkeleton, AnalysisResultLevel } from './analysis-level-bar';
|
||||
import { UserAnalysisElement } from '@/packages/features/accounts/src/types/accounts';
|
||||
import { AnalysisElement } from '~/lib/services/analysis-element.service';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
export enum AnalysisStatus {
|
||||
NORMAL = 0,
|
||||
@@ -15,31 +18,27 @@ export enum AnalysisStatus {
|
||||
}
|
||||
|
||||
const Analysis = ({
|
||||
analysis: {
|
||||
name,
|
||||
status,
|
||||
unit,
|
||||
value,
|
||||
normLowerIncluded,
|
||||
normUpperIncluded,
|
||||
normLower,
|
||||
normUpper,
|
||||
},
|
||||
analysisElement,
|
||||
results,
|
||||
}: {
|
||||
analysis: {
|
||||
name: string;
|
||||
status: AnalysisStatus;
|
||||
unit: string;
|
||||
value: number;
|
||||
normLowerIncluded: boolean;
|
||||
normUpperIncluded: boolean;
|
||||
normLower: number;
|
||||
normUpper: number;
|
||||
};
|
||||
analysisElement: AnalysisElement;
|
||||
results?: UserAnalysisElement;
|
||||
}) => {
|
||||
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 isUnderNorm = value < normLower;
|
||||
const getAnalysisResultLevel = () => {
|
||||
if (!results) {
|
||||
return null;
|
||||
}
|
||||
if (isUnderNorm) {
|
||||
switch (status) {
|
||||
case AnalysisStatus.MEDIUM:
|
||||
@@ -59,7 +58,7 @@ const Analysis = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="border-border grid grid-cols-2 items-center justify-between rounded-lg border px-5 py-3 sm:flex">
|
||||
<div className="border-border items-center justify-between rounded-lg border px-5 py-3 sm:h-[65px] flex flex-col sm:flex-row px-12 gap-2 sm:gap-0">
|
||||
<div className="flex items-center gap-2 font-semibold">
|
||||
{name}
|
||||
<div
|
||||
@@ -78,19 +77,35 @@ const Analysis = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="font-semibold">{value}</div>
|
||||
<div className="text-muted-foreground text-sm">{unit}</div>
|
||||
</div>
|
||||
<div className="text-muted-foreground mt-4 flex gap-2 text-center text-sm sm:mt-0 sm:block sm:gap-0">
|
||||
{normLower} - {normUpper}
|
||||
<div>Normaalne vahemik</div>
|
||||
</div>
|
||||
<AnalysisLevelBar
|
||||
normLowerIncluded={normLowerIncluded}
|
||||
normUpperIncluded={normUpperIncluded}
|
||||
level={getAnalysisResultLevel()}
|
||||
/>
|
||||
{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 flex flex-col-reverse gap-2 text-center text-sm sm:block sm:gap-0 mx-8">
|
||||
{normLower} - {normUpper}
|
||||
<div>
|
||||
<Trans i18nKey="analysis-results:results.range.normal" />
|
||||
</div>
|
||||
</div>
|
||||
<AnalysisLevelBar
|
||||
normLowerIncluded={normLowerIncluded}
|
||||
normUpperIncluded={normUpperIncluded}
|
||||
level={getAnalysisResultLevel()!}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex items-center gap-3 sm:ml-auto">
|
||||
<div className="font-semibold">
|
||||
<Trans i18nKey="analysis-results:waitingForResults" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-[60px] mx-8"></div>
|
||||
<AnalysisLevelBarSkeleton />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Fragment } from 'react';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { createI18nServerInstance } from '@/lib/i18n/i18n.server';
|
||||
import { withI18n } from '@/lib/i18n/with-i18n';
|
||||
|
||||
@@ -7,11 +8,17 @@ import { PageBody } from '@kit/ui/page';
|
||||
import { Button } from '@kit/ui/shadcn/button';
|
||||
|
||||
import { loadUserAnalysis } from '../../_lib/server/load-user-analysis';
|
||||
import Analysis, { AnalysisStatus } from './_components/analysis';
|
||||
import Analysis from './_components/analysis';
|
||||
import { listProductTypes } from '@lib/data/products';
|
||||
import pathsConfig from '~/config/paths.config';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { getOrders } from '~/lib/services/order.service';
|
||||
import { AnalysisElement, getAnalysisElements } from '~/lib/services/analysis-element.service';
|
||||
import type { UserAnalysisElement } from '@kit/accounts/types/accounts';
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
const i18n = await createI18nServerInstance();
|
||||
const title = i18n.t('account:analysisResults.pageTitle');
|
||||
const title = i18n.t('analysis-results:pageTitle');
|
||||
|
||||
return {
|
||||
title,
|
||||
@@ -21,45 +28,56 @@ export const generateMetadata = async () => {
|
||||
async function AnalysisResultsPage() {
|
||||
const analysisList = await loadUserAnalysis();
|
||||
|
||||
const orders = await getOrders().catch(() => null);
|
||||
const { productTypes } = await listProductTypes();
|
||||
|
||||
if (!orders || !productTypes) {
|
||||
redirect(pathsConfig.auth.signIn);
|
||||
}
|
||||
|
||||
const analysisElementIds = [
|
||||
...new Set(orders?.flatMap((order) => order.analysis_element_ids).filter(Boolean) as number[]),
|
||||
];
|
||||
const analysisElements = await getAnalysisElements({ ids: analysisElementIds });
|
||||
const analysisElementsWithResults = analysisElements.reduce((acc, curr) => {
|
||||
const analysisResponseWithElementResults = analysisList?.find((result) => result.elements.some((element) => element.analysis_element_original_id === curr.analysis_id_original));
|
||||
const elementResults = analysisResponseWithElementResults?.elements.find((element) => element.analysis_element_original_id === curr.analysis_id_original) as UserAnalysisElement | undefined;
|
||||
return {
|
||||
...acc,
|
||||
[curr.id]: {
|
||||
analysisElement: curr,
|
||||
results: elementResults,
|
||||
},
|
||||
}
|
||||
}, {} as Record<number, { analysisElement: AnalysisElement; results: UserAnalysisElement | undefined }>);
|
||||
|
||||
return (
|
||||
<PageBody>
|
||||
<div className="mt-8 flex items-center justify-between">
|
||||
<div className="mt-8 flex flex-col sm:flex-row sm:items-center justify-between gap-4 sm:gap-0">
|
||||
<div>
|
||||
<h4>
|
||||
<Trans i18nKey="account:analysisResults.pageTitle" />
|
||||
<Trans i18nKey="analysis-results:pageTitle" />
|
||||
</h4>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{analysisList && analysisList.length > 0 ? (
|
||||
<Trans i18nKey="account:analysisResults.description" />
|
||||
<Trans i18nKey="analysis-results:description" />
|
||||
) : (
|
||||
<Trans i18nKey="account:analysisResults.descriptionEmpty" />
|
||||
<Trans i18nKey="analysis-results:descriptionEmpty" />
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Button>
|
||||
<Trans i18nKey="account:analysisResults.orderNewAnalysis" />
|
||||
<Button asChild>
|
||||
<Link href={pathsConfig.app.orderAnalysis}>
|
||||
<Trans i18nKey="analysis-results:orderNewAnalysis" />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
{analysisList?.map((analysis) => (
|
||||
<Fragment key={analysis.id}>
|
||||
{analysis.elements.map((element) => (
|
||||
<Analysis
|
||||
key={element.id}
|
||||
analysis={{
|
||||
name: element.analysis_name || '',
|
||||
status: element.norm_status as AnalysisStatus,
|
||||
unit: element.unit || '',
|
||||
value: element.response_value,
|
||||
normLowerIncluded: !!element.norm_lower_included,
|
||||
normUpperIncluded: !!element.norm_upper_included,
|
||||
normLower: element.norm_lower || 0,
|
||||
normUpper: element.norm_upper || 0,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Fragment>
|
||||
))}
|
||||
{Object.entries(analysisElementsWithResults).map(([id, { analysisElement, results }]) => {
|
||||
return (
|
||||
<Analysis key={id} analysisElement={analysisElement} results={results} />
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</PageBody>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user