From 6dcc91a20654faa60fdb67997fc705d2cbe1bf38 Mon Sep 17 00:00:00 2001 From: Danel Kungla Date: Tue, 21 Oct 2025 09:36:29 +0300 Subject: [PATCH] WIP: add lifestyle block --- .../(dashboard)/order-analysis/page.tsx | 4 +- app/home/(user)/(dashboard)/page.tsx | 23 ++-- app/home/(user)/_components/ai/ai-blocks.tsx | 42 ++++++ .../(user)/_components/ai/life-style-card.tsx | 19 +++ .../ai/order-analyses-package-card.tsx | 51 +++++++ .../{ => ai}/recommendations-skeleton.tsx | 0 .../_components/{ => ai}/recommendations.tsx | 10 +- .../_components/order-analyses-cards.tsx | 128 ++++++++---------- .../(user)/_lib/server/load-life-style.ts | 41 ++++++ public/locales/et/dashboard.json | 4 + 10 files changed, 233 insertions(+), 89 deletions(-) create mode 100644 app/home/(user)/_components/ai/ai-blocks.tsx create mode 100644 app/home/(user)/_components/ai/life-style-card.tsx create mode 100644 app/home/(user)/_components/ai/order-analyses-package-card.tsx rename app/home/(user)/_components/{ => ai}/recommendations-skeleton.tsx (100%) rename app/home/(user)/_components/{ => ai}/recommendations.tsx (65%) create mode 100644 app/home/(user)/_lib/server/load-life-style.ts diff --git a/app/home/(user)/(dashboard)/order-analysis/page.tsx b/app/home/(user)/(dashboard)/order-analysis/page.tsx index edd0ad7..5e2bd2f 100644 --- a/app/home/(user)/(dashboard)/order-analysis/page.tsx +++ b/app/home/(user)/(dashboard)/order-analysis/page.tsx @@ -37,8 +37,8 @@ async function OrderAnalysisPage() { return ( <> } - description={} + title={} + description={} /> diff --git a/app/home/(user)/(dashboard)/page.tsx b/app/home/(user)/(dashboard)/page.tsx index cdb7bcf..49db975 100644 --- a/app/home/(user)/(dashboard)/page.tsx +++ b/app/home/(user)/(dashboard)/page.tsx @@ -1,5 +1,3 @@ -import { Suspense } from 'react'; - import { redirect } from 'next/navigation'; import { toTitleCase } from '@/lib/utils'; @@ -12,11 +10,9 @@ import { createUserAnalysesApi } from '@kit/user-analyses/api'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; +import AIBlocks from '../_components/ai/ai-blocks'; import Dashboard from '../_components/dashboard'; import DashboardCards from '../_components/dashboard-cards'; -import Recommendations from '../_components/recommendations'; -import RecommendationsSkeleton from '../_components/recommendations-skeleton'; -import { isValidOpenAiEnv } from '../_lib/server/is-valid-open-ai-env'; import { loadCurrentUserAccount } from '../_lib/server/load-user-account'; export const generateMetadata = async () => { @@ -53,16 +49,13 @@ async function UserHomePage() { /> - {(await isValidOpenAiEnv()) && ( - <> -

- -

- }> - - - - )} + +

+ +

+
+ +
); diff --git a/app/home/(user)/_components/ai/ai-blocks.tsx b/app/home/(user)/_components/ai/ai-blocks.tsx new file mode 100644 index 0000000..fe1ff9d --- /dev/null +++ b/app/home/(user)/_components/ai/ai-blocks.tsx @@ -0,0 +1,42 @@ +'use server'; + +import React, { Suspense } from 'react'; + +import { AccountWithParams } from '@/packages/features/accounts/src/types/accounts'; + +import { isValidOpenAiEnv } from '../../_lib/server/is-valid-open-ai-env'; +import { loadAnalyses } from '../../_lib/server/load-analyses'; +import LifeStyleCard from './life-style-card'; +import OrderAnalysesPackageCard from './order-analyses-package-card'; +import Recommendations from './recommendations'; +import RecommendationsSkeleton from './recommendations-skeleton'; + +const AIBlocks = async ({ account }: { account: AccountWithParams }) => { + const isOpenAiAvailable = await isValidOpenAiEnv(); + + if (!isOpenAiAvailable) { + return ; + } + + const { analyses, countryCode } = await loadAnalyses(); + + if (analyses.length === 0) { + return ( + <> + + }> + + + + ); + } + + return ( + }> + + + + ); +}; + +export default AIBlocks; diff --git a/app/home/(user)/_components/ai/life-style-card.tsx b/app/home/(user)/_components/ai/life-style-card.tsx new file mode 100644 index 0000000..e2fffd0 --- /dev/null +++ b/app/home/(user)/_components/ai/life-style-card.tsx @@ -0,0 +1,19 @@ +'use server'; + +import React from 'react'; + +import { Card } from '@kit/ui/shadcn/card'; + +import { loadLifeStyle } from '../../_lib/server/load-life-style'; + +const LifeStyleCard = async () => { + const data = await loadLifeStyle(); + + return ( + + Test + + ); +}; + +export default LifeStyleCard; diff --git a/app/home/(user)/_components/ai/order-analyses-package-card.tsx b/app/home/(user)/_components/ai/order-analyses-package-card.tsx new file mode 100644 index 0000000..a27339e --- /dev/null +++ b/app/home/(user)/_components/ai/order-analyses-package-card.tsx @@ -0,0 +1,51 @@ +import React from 'react'; + +import Link from 'next/link'; + +import { pathsConfig } from '@/packages/shared/src/config'; +import { ChevronRight, HeartPulse } from 'lucide-react'; + +import { Trans } from '@kit/ui/makerkit/trans'; +import { Button } from '@kit/ui/shadcn/button'; +import { + Card, + CardDescription, + CardFooter, + CardHeader, +} from '@kit/ui/shadcn/card'; + +const OrderAnalysesPackageCard = () => { + return ( + + +
+ +
+
+ + + +
+
+ +
+ +
+ + + +
+
+ ); +}; + +export default OrderAnalysesPackageCard; diff --git a/app/home/(user)/_components/recommendations-skeleton.tsx b/app/home/(user)/_components/ai/recommendations-skeleton.tsx similarity index 100% rename from app/home/(user)/_components/recommendations-skeleton.tsx rename to app/home/(user)/_components/ai/recommendations-skeleton.tsx diff --git a/app/home/(user)/_components/recommendations.tsx b/app/home/(user)/_components/ai/recommendations.tsx similarity index 65% rename from app/home/(user)/_components/recommendations.tsx rename to app/home/(user)/_components/ai/recommendations.tsx index 71403ef..b8d2722 100644 --- a/app/home/(user)/_components/recommendations.tsx +++ b/app/home/(user)/_components/ai/recommendations.tsx @@ -4,9 +4,9 @@ import React from 'react'; import { AccountWithParams } from '@/packages/features/accounts/src/types/accounts'; -import { loadAnalyses } from '../_lib/server/load-analyses'; -import { loadRecommendations } from '../_lib/server/load-recommendations'; -import OrderAnalysesCards from './order-analyses-cards'; +import { loadAnalyses } from '../../_lib/server/load-analyses'; +import { loadRecommendations } from '../../_lib/server/load-recommendations'; +import OrderAnalysesCards from '../order-analyses-cards'; export default async function Recommendations({ account, @@ -25,6 +25,8 @@ export default async function Recommendations({ } return ( - + <> + + ); } diff --git a/app/home/(user)/_components/order-analyses-cards.tsx b/app/home/(user)/_components/order-analyses-cards.tsx index 88574fa..b6f73e3 100644 --- a/app/home/(user)/_components/order-analyses-cards.tsx +++ b/app/home/(user)/_components/order-analyses-cards.tsx @@ -57,72 +57,64 @@ export default function OrderAnalysesCards({ } }; - return ( -
- {analyses.map(({ title, variant, description, subtitle, price }) => { - const formattedPrice = - typeof price === 'number' - ? formatCurrency({ - currencyCode: 'eur', - locale: language, - value: price, - }) - : null; - return ( - - -
- -
-
- -
-
- -
-
- {title} - {description && ( - <> - - {formattedPrice} - {description} -
- } - /> - - )} - - {subtitle && {subtitle}} -
-
- {formattedPrice} -
- - - ); - })} - - ); + return analyses.map(({ title, variant, description, subtitle, price }) => { + const formattedPrice = + typeof price === 'number' + ? formatCurrency({ + currencyCode: 'eur', + locale: language, + value: price, + }) + : null; + return ( + + +
+ +
+
+ +
+
+ +
+
+ {title} + {description && ( + <> + + {formattedPrice} + {description} +
+ } + /> + + )} + + {subtitle && {subtitle}} + +
+ {formattedPrice} +
+
+
+ ); + }); } diff --git a/app/home/(user)/_lib/server/load-life-style.ts b/app/home/(user)/_lib/server/load-life-style.ts new file mode 100644 index 0000000..631ffd4 --- /dev/null +++ b/app/home/(user)/_lib/server/load-life-style.ts @@ -0,0 +1,41 @@ +import { cache } from 'react'; + +import { AccountWithParams } from '@/packages/features/accounts/src/types/accounts'; +import OpenAI from 'openai'; + +import PersonalCode from '~/lib/utils'; + +const failedResponse = { + lifeStyle: null, + summary: null, +}; + +async function lifeStyleLoader(account: AccountWithParams) { + if (!account?.personal_code) { + return failedResponse; + } + + const openAIClient = new OpenAI(); + const { gender, age } = PersonalCode.parsePersonalCode(account.personal_code); + try { + const response = await openAIClient.responses.create({ + store: false, + prompt: { + id: analysesRecommendationsPromptId, + variables: { + analyses: JSON.stringify(formattedAnalyses), + results: JSON.stringify(formattedAnalysisResponses), + gender: gender.value, + age: age.toString(), + weight: weight.toString(), + }, + }, + }); + + return response; + } catch (error) { + console.error('Error calling OpenAI: ', error); + return failedResponse; + } +} +export const loadLifeStyle = cache(lifeStyleLoader); diff --git a/public/locales/et/dashboard.json b/public/locales/et/dashboard.json index 8fce772..ffc7aa7 100644 --- a/public/locales/et/dashboard.json +++ b/public/locales/et/dashboard.json @@ -21,6 +21,10 @@ "benefits": { "title": "Sinu Medreport konto seis", "validUntil": "Kehtiv kuni {{date}}" + }, + "orderPackage": { + "title": "Telli analüüside pakett", + "description": "Võrdle erinevate pakettide vahel ja vali endale sobiv" } }, "recommendations": {