diff --git a/app/home/(user)/(dashboard)/analysis-results/test/test-responses.ts b/app/home/(user)/(dashboard)/analysis-results/test/test-responses.ts index e4da467..1d4768d 100644 --- a/app/home/(user)/(dashboard)/analysis-results/test/test-responses.ts +++ b/app/home/(user)/(dashboard)/analysis-results/test/test-responses.ts @@ -1,840 +1,840 @@ -import { AnalysisResultDetailsMapped } from "@/packages/features/accounts/src/types/analysis-results"; +import { AnalysisResultDetailsMapped } from '@/packages/features/accounts/src/types/analysis-results'; -type AnalysisTestResponse = Omit; +export type AnalysisTestResponse = Omit< + AnalysisResultDetailsMapped, + 'order' | 'orderedAnalysisElementIds' | 'summary' | 'elements' +>; const empty1: AnalysisTestResponse = { - "id": 1, - "orderedAnalysisElements": [], + id: 1, + orderedAnalysisElements: [], }; const big1: AnalysisTestResponse = { - "id": 2, - "orderedAnalysisElements": [ + 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" + 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": [ + 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: '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": [ + 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: '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: '%', + 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: '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: '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: '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: '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: '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: '%', + 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: '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: '%', + 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: 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: '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.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: 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: 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: 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: 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: '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: '%', + 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: '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" - } + 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" - } + 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: '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" - } - } + 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, -]; +export const analysisResponses: AnalysisTestResponse[] = [empty1, big1, big2]; diff --git a/app/home/(user)/(dashboard)/page.tsx b/app/home/(user)/(dashboard)/page.tsx index 65d3a67..f3e603c 100644 --- a/app/home/(user)/(dashboard)/page.tsx +++ b/app/home/(user)/(dashboard)/page.tsx @@ -1,7 +1,7 @@ import { redirect } from 'next/navigation'; import { toTitleCase } from '@/lib/utils'; -import { createUserAnalysesApi } from '@kit/user-analyses/api'; +import { createUserAnalysesApi } from '@/packages/features/user-analyses/src/server/api'; import { getSupabaseServerClient } from '@/packages/supabase/src/clients/server-client'; import { PageBody, PageHeader } from '@kit/ui/page'; @@ -13,7 +13,10 @@ import { withI18n } from '~/lib/i18n/with-i18n'; import Dashboard from '../_components/dashboard'; import DashboardCards from '../_components/dashboard-cards'; import Recommendations from '../_components/recommendations'; +import { loadAnalyses } from '../_lib/server/load-analyses'; +import { loadRecommendations } from '../_lib/server/load-recommendations'; import { loadCurrentUserAccount } from '../_lib/server/load-user-account'; +import { analysisResponses } from './analysis-results/test/test-responses'; export const generateMetadata = async () => { const i18n = await createI18nServerInstance(); @@ -31,6 +34,15 @@ async function UserHomePage() { const { account } = await loadCurrentUserAccount(); const api = createUserAnalysesApi(client); const bmiThresholds = await api.fetchBmiThresholds(); + const { analyses, countryCode } = await loadAnalyses(); + + const analysisRecommendations = await loadRecommendations( + analysisResponses, + analyses, + account, + ); + + console.log('analysisRecommendations', analysisRecommendations); if (!account) { redirect('/'); @@ -51,7 +63,12 @@ async function UserHomePage() { - + + analysisRecommendations.includes(analysis.title), + )} + countryCode={countryCode} + /> ); diff --git a/app/home/(user)/_components/recommendations.tsx b/app/home/(user)/_components/recommendations.tsx index 80bc725..d9eace1 100644 --- a/app/home/(user)/_components/recommendations.tsx +++ b/app/home/(user)/_components/recommendations.tsx @@ -1,26 +1,24 @@ 'use client'; -import React, { useEffect } from 'react'; +import React from 'react'; -import OpenAI from 'openai'; +import OrderAnalysesCards, { OrderAnalysisCard } from './order-analyses-cards'; -export default function Recommendations() { - // const client = new OpenAI(); +export default function Recommendations({ + recommended, + countryCode, +}: { + recommended: OrderAnalysisCard[]; + countryCode: string; +}) { + if (recommended.length < 1) { + return null; + } - const getRecommendations = async () => { - // const response = await client.responses.create({ - // model: 'gpt-5', - // input: 'Write a short bedtime story about a unicorn.', - // }); - // console.log(response.output_text); - }; - - // useEffect(() => { - // console.log('process.env', process.env); - // if (process.env.OPENAI_API_KEY) { - // getRecommendations(); - // } - // }, []); - - return
Recommendations
; + return ( +
+

Medreport soovitab teile

+ +
+ ); } diff --git a/app/home/(user)/_lib/server/load-recommendations.test.ts b/app/home/(user)/_lib/server/load-recommendations.test.ts new file mode 100644 index 0000000..b492eae --- /dev/null +++ b/app/home/(user)/_lib/server/load-recommendations.test.ts @@ -0,0 +1,133 @@ +import type { Mock } from 'jest-mock'; + +// ---- Mocks you can tweak per test ---- +const createResponseMock = jest.fn(); +const getLatestResponseTimeMock = jest.fn(); +const getLatestUniqueAnalysResponsesMock = jest.fn(); +const parsePersonalCodeMock = jest.fn(() => ({ gender: { value: 'male' } })); + +// Mock OpenAI SDK +jest.mock('openai', () => { + return { + __esModule: true, + default: class OpenAI { + responses = { create: createResponseMock }; + }, + }; +}); + +// Mock next/cache (global cache map so it persists between calls) +const globalCache = new Map(); +jest.mock('next/cache', () => { + return { + __esModule: true, + unstable_cache: + (fn: (...args: any[]) => Promise, keyParts: any[], _opts?: any) => + async (...args: any[]) => { + const key = JSON.stringify(keyParts); + if (globalCache.has(key)) return globalCache.get(key); + const val = await fn(...args); + globalCache.set(key, val); + return val; + }, + }; +}); + +// Mock your analysis helpers + personal code parser +jest.mock('../src/analysis-utils', () => ({ + __esModule: true, + getLatestUniqueAnalysResponses: getLatestUniqueAnalysResponsesMock, + getLatestResponseTime: getLatestResponseTimeMock, +})); +jest.mock('../src/personal-code', () => ({ + __esModule: true, + PersonalCode: { parsePersonalCode: parsePersonalCodeMock }, +})); + +describe('loadRecommendations', () => { + beforeEach(() => { + createResponseMock.mockReset(); + getLatestResponseTimeMock.mockReset(); + getLatestUniqueAnalysResponsesMock.mockReset(); + globalCache.clear(); + }); + + it('should call OpenAI once when latest date stays the same (cache hit on 2nd call)', async () => { + const date1 = new Date('2025-09-16T12:00:00Z'); + getLatestResponseTimeMock.mockReturnValue(date1); + getLatestUniqueAnalysResponsesMock.mockImplementation((arr: any[]) => arr); + + createResponseMock.mockResolvedValue({ + output_text: JSON.stringify({ recommended: ['A', 'B'] }), + }); + + const { loadRecommendations } = await import('./load-recommendations'); + + const analysisResponses = [ + { name: 'x', value: 1, responseTime: date1 }, + ] as any[]; + const analyses = [{ title: 't', description: 'd' }] as any[]; + const account = { id: 'u1', personal_code: '12345678901' } as any; + + // Act: 1st call (MISS) + const out1 = await loadRecommendations( + analysisResponses, + analyses, + account, + ); + + // Act: 2nd call with same date (HIT) + const out2 = await loadRecommendations( + analysisResponses, + analyses, + account, + ); + + // Assert: only one API call, result reused + expect(createResponseMock).toHaveBeenCalledTimes(1); + expect(out1).toEqual(['A', 'B']); + expect(out2).toEqual(['A', 'B']); + }); + + it('should call OpenAI again when latest date changes (new cache key)', async () => { + const date1 = new Date('2025-09-16T12:00:00Z'); + const date2 = new Date('2025-09-17T00:00:00Z'); + + getLatestResponseTimeMock.mockReturnValueOnce(date1); + getLatestResponseTimeMock.mockReturnValueOnce(date2); + + getLatestUniqueAnalysResponsesMock.mockImplementation((arr: any[]) => arr); + + createResponseMock + .mockResolvedValueOnce({ + output_text: JSON.stringify({ recommended: ['A', 'B'] }), + }) + .mockResolvedValueOnce({ + output_text: JSON.stringify({ recommended: ['C'] }), + }); + + const { loadRecommendations } = await import('./load-recommendations'); + + const analysisResponses = [ + { name: 'x', value: 1, responseTime: date1 }, + ] as any[]; + const analyses = [{ title: 't', description: 'd' }] as any[]; + const account = { id: 'u1', personal_code: '12345678901' } as any; + + const out1 = await loadRecommendations( + analysisResponses, + analyses, + account, + ); + + const out2 = await loadRecommendations( + analysisResponses, + analyses, + account, + ); + + expect(createResponseMock).toHaveBeenCalledTimes(2); + expect(out1).toEqual(['A', 'B']); + expect(out2).toEqual(['C']); + }); +}); diff --git a/app/home/(user)/_lib/server/load-recommendations.ts b/app/home/(user)/_lib/server/load-recommendations.ts new file mode 100644 index 0000000..ed8e6f1 --- /dev/null +++ b/app/home/(user)/_lib/server/load-recommendations.ts @@ -0,0 +1,153 @@ +import { cache } from 'react'; + +import { unstable_cache } from 'next/cache'; + +import { AccountWithParams } from '@/packages/features/accounts/src/types/accounts'; +import crypto from 'crypto'; +import OpenAI from 'openai'; + +import PersonalCode from '~/lib/utils'; + +import { AnalysisTestResponse } from '../../(dashboard)/analysis-results/test/test-responses'; +import { OrderAnalysisCard } from '../../_components/order-analyses-cards'; + +export const loadRecommendations = cache(recommendationsLoader); + +type FormattedAnalysisResponse = { + value: string; + name: string; + responseTime: string; +}; + +function canonical(v: unknown): string { + return JSON.stringify(v, (_k, val) => { + if (val && typeof val === 'object' && !Array.isArray(val)) { + // sort object keys for stable JSON + return Object.keys(val) + .sort() + .reduce((o: any, k) => ((o[k] = (val as any)[k]), o), {}); + } + return val; + }); +} +const sha256 = (s: string) => + crypto.createHash('sha256').update(s).digest('hex'); + +const getLatestResponseTime = (items: FormattedAnalysisResponse[]) => { + if (!items?.length) return null; + + let latest = null; + for (const it of items) { + const d = new Date(it.responseTime); + const t = d.getTime(); + if (!Number.isNaN(t) && (latest === null || t > latest.getTime())) { + latest = d; + } + } + return latest; +}; + +const getLatestUniqueAnalysResponses = ( + analysisResponses: AnalysisTestResponse[], +): { name: string; value: string; responseTime: string }[] => { + const analysisElements = analysisResponses + .map(({ orderedAnalysisElements }) => orderedAnalysisElements) + .flat(); + + console.log('analysisElements', analysisElements.length); + + const map = new Map(); + for (const it of analysisElements) { + const prev = map.get(it.analysisName); + if (it.results.responseTime) { + if ( + !prev || + new Date(it.results.responseTime) > new Date(prev.responseTime) + ) { + map.set(it.analysisName, { + name: it.analysisName, + value: it.results.responseValue, + responseTime: it.results.responseTime, + }); + } + } + } + return [...map.values()]; +}; + +async function recommendationsLoader( + analysisResponses: AnalysisTestResponse[], + analyses: OrderAnalysisCard[], + account: AccountWithParams | null, +): Promise { + if (!account?.personal_code) { + return []; + } + + const client = new OpenAI(); + const { gender } = PersonalCode.parsePersonalCode(account.personal_code); + console.log('analysisResponses', analysisResponses); + console.log('analyises', analyses); + const latestUniqueAnalysResponses = + getLatestUniqueAnalysResponses(analysisResponses); + const latestResponseTime = getLatestResponseTime(latestUniqueAnalysResponses); + const latestISO = latestResponseTime + ? new Date(latestResponseTime).toISOString() + : 'none'; + console.log('latestResponseTime', latestResponseTime); + const formattedAnalysisResponses = latestUniqueAnalysResponses.map( + ({ name, value }) => ({ name, value }), + ); + const formattedAnalyses = analyses.map(({ description, title }) => ({ + description, + title, + })); + + console.log('formattedAnalyses', JSON.stringify(formattedAnalyses)); + console.log( + 'latestUniqueAnalysResponses', + JSON.stringify(latestUniqueAnalysResponses), + ); + const response = await client.responses.create({ + model: 'gpt-5', + store: false, + prompt: { + id: 'pmpt_68ca9c8bfa8c8193b27eadc6496c36440df449ece4f5a8dd', + variables: { + analyses: JSON.stringify(formattedAnalyses), + results: JSON.stringify(formattedAnalysisResponses), + gender: gender.value, + }, + }, + }); + + const responseJson = JSON.parse(response.output_text); + console.log('responseJson: ', responseJson); + + const keyPayload = { + model: 'gpt-5', // swap to a model your project can access + promptId: 'pmpt_68ca9c8bfa8c8193b27eadc6496c36440df449ece4f5a8dd', + latestISO, + }; + + const key = 'recs:' + sha256(canonical(keyPayload)); + + const run = unstable_cache(async () => { + const response = await client.responses.create({ + model: keyPayload.model, + store: false, + prompt: { + id: keyPayload.promptId, + variables: { + analyses: JSON.stringify(formattedAnalyses), + results: JSON.stringify(latestUniqueAnalysResponses), + gender: gender.value, + }, + }, + }); + const json = JSON.parse(response.output_text); + return json.recommended; + }, ['recommendations', key]); + + return await run(); +} diff --git a/app/home/[account]/page.tsx b/app/home/[account]/page.tsx index 8279c92..12d9715 100644 --- a/app/home/[account]/page.tsx +++ b/app/home/[account]/page.tsx @@ -4,8 +4,8 @@ import { use } from 'react'; import { CompanyGuard } from '@/packages/features/team-accounts/src/components'; import { createTeamAccountsApi } from '@/packages/features/team-accounts/src/server/api'; +import { createUserAnalysesApi } from '@/packages/features/user-analyses/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';