diff --git a/.npmrc b/.npmrc index dad7c34..d82064f 100644 --- a/.npmrc +++ b/.npmrc @@ -3,6 +3,7 @@ dedupe-peer-dependents=true use-lockfile-v6=true resolution-mode=highest package-manager-strict=true +ignore-workspace-root-check=true public-hoist-pattern[]=*i18next* public-hoist-pattern[]=*eslint* public-hoist-pattern[]=*prettier* diff --git a/README.md b/README.md index f3cca27..1125158 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,18 @@ if you get missing dependency error do `pnpm i --force` pnpm add -w ``` +## Eslint and prettier + +To clean code run + +```bash +npm run lint:fix +``` + +```bash +npm run prettier:fix +``` + ## Supabase Start supabase in docker diff --git a/app/(marketing)/(legal)/privacy-policy/page.tsx b/app/(marketing)/(legal)/privacy-policy/page.tsx index 995210d..b8ff856 100644 --- a/app/(marketing)/(legal)/privacy-policy/page.tsx +++ b/app/(marketing)/(legal)/privacy-policy/page.tsx @@ -2,7 +2,6 @@ import { SitePageHeader } from '~/(marketing)/_components/site-page-header'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; - export async function generateMetadata() { const { t } = await createI18nServerInstance(); diff --git a/app/(marketing)/(legal)/terms-of-service/page.tsx b/app/(marketing)/(legal)/terms-of-service/page.tsx index 6de2269..ee7d0cb 100644 --- a/app/(marketing)/(legal)/terms-of-service/page.tsx +++ b/app/(marketing)/(legal)/terms-of-service/page.tsx @@ -2,7 +2,6 @@ import { SitePageHeader } from '~/(marketing)/_components/site-page-header'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; - export async function generateMetadata() { const { t } = await createI18nServerInstance(); diff --git a/app/(marketing)/_components/site-header-account-section.tsx b/app/(marketing)/_components/site-header-account-section.tsx index dd62722..2d11663 100644 --- a/app/(marketing)/_components/site-header-account-section.tsx +++ b/app/(marketing)/_components/site-header-account-section.tsx @@ -7,15 +7,14 @@ import { UserWorkspace } from '@/app/home/(user)/_lib/server/load-user-workspace import { useQuery } from '@tanstack/react-query'; import { PersonalAccountDropdown } from '@kit/accounts/personal-account-dropdown'; +import { featureFlagsConfig, pathsConfig } from '@kit/shared/config'; +import { useAuthConfig } from '@kit/shared/hooks'; import { useSignOut } from '@kit/supabase/hooks/use-sign-out'; import { useSupabase } from '@kit/supabase/hooks/use-supabase'; import { Button } from '@kit/ui/button'; import { If } from '@kit/ui/if'; import { Trans } from '@kit/ui/trans'; -import { featureFlagsConfig, pathsConfig } from '@kit/shared/config'; -import { useAuthConfig } from '@kit/shared/hooks'; - const ModeToggle = dynamic(() => import('@kit/ui/mode-toggle').then((mod) => ({ default: mod.ModeToggle, diff --git a/app/(marketing)/_components/site-header.tsx b/app/(marketing)/_components/site-header.tsx index 57d5192..694239d 100644 --- a/app/(marketing)/_components/site-header.tsx +++ b/app/(marketing)/_components/site-header.tsx @@ -1,8 +1,7 @@ import { UserWorkspace } from '@/app/home/(user)/_lib/server/load-user-workspace'; -import { Header } from '@kit/ui/marketing'; - import { AppLogo } from '@kit/shared/components/app-logo'; +import { Header } from '@kit/ui/marketing'; import { SiteHeaderAccountSection } from './site-header-account-section'; diff --git a/app/(marketing)/blog/[slug]/page.tsx b/app/(marketing)/blog/[slug]/page.tsx index 76d48cb..8c4ce1c 100644 --- a/app/(marketing)/blog/[slug]/page.tsx +++ b/app/(marketing)/blog/[slug]/page.tsx @@ -8,7 +8,6 @@ import { createCmsClient } from '@kit/cms'; import { withI18n } from '~/lib/i18n/with-i18n'; - import { Post } from '../../blog/_components/post'; interface BlogPageProps { diff --git a/app/(marketing)/contact/_lib/server/server-actions.ts b/app/(marketing)/contact/_lib/server/server-actions.ts index 3b07206..dad3610 100644 --- a/app/(marketing)/contact/_lib/server/server-actions.ts +++ b/app/(marketing)/contact/_lib/server/server-actions.ts @@ -11,14 +11,16 @@ const contactEmail = z .string({ error: 'Contact email is required. Please use the environment variable CONTACT_EMAIL.', - }).describe(`The email where you want to receive the contact form submissions.`) + }) + .describe(`The email where you want to receive the contact form submissions.`) .parse(process.env.CONTACT_EMAIL); const emailFrom = z .string({ error: 'Sender email is required. Please use the environment variable EMAIL_SENDER.', - }).describe(`The email sending address.`) + }) + .describe(`The email sending address.`) .parse(process.env.EMAIL_SENDER); export const sendContactEmail = enhanceAction( diff --git a/app/(marketing)/contact/page.tsx b/app/(marketing)/contact/page.tsx index 6a6f516..8597d77 100644 --- a/app/(marketing)/contact/page.tsx +++ b/app/(marketing)/contact/page.tsx @@ -6,7 +6,6 @@ import { ContactForm } from '~/(marketing)/contact/_components/contact-form'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; - export async function generateMetadata() { const { t } = await createI18nServerInstance(); diff --git a/app/(marketing)/faq/page.tsx b/app/(marketing)/faq/page.tsx index 3fcc944..5c8beee 100644 --- a/app/(marketing)/faq/page.tsx +++ b/app/(marketing)/faq/page.tsx @@ -9,7 +9,6 @@ import { SitePageHeader } from '~/(marketing)/_components/site-page-header'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; - export const generateMetadata = async () => { const { t } = await createI18nServerInstance(); diff --git a/app/(marketing)/layout.tsx b/app/(marketing)/layout.tsx index 5b308e2..4a41c52 100644 --- a/app/(marketing)/layout.tsx +++ b/app/(marketing)/layout.tsx @@ -2,9 +2,8 @@ import { use } from 'react'; import { SiteFooter } from '~/(marketing)/_components/site-footer'; import { SiteHeader } from '~/(marketing)/_components/site-header'; -import { withI18n } from '~/lib/i18n/with-i18n'; - import { loadCurrentUserAccounts } from '~/home/(user)/_lib/server/load-accounts'; +import { withI18n } from '~/lib/i18n/with-i18n'; function SiteLayout(props: React.PropsWithChildren) { const accounts = use(loadCurrentUserAccounts()); diff --git a/app/(marketing)/page.tsx b/app/(marketing)/page.tsx index 1034428..722aa55 100644 --- a/app/(marketing)/page.tsx +++ b/app/(marketing)/page.tsx @@ -1,9 +1,9 @@ import Link from 'next/link'; +import { ArrowRightIcon } from 'lucide-react'; + import { MedReportLogo } from '@kit/shared/components/med-report-logo'; import { pathsConfig } from '@kit/shared/config'; -import { ArrowRightIcon } from 'lucide-react'; - import { CtaButton, Hero } from '@kit/ui/marketing'; import { Trans } from '@kit/ui/trans'; diff --git a/app/(public)/company-offer/page.tsx b/app/(public)/company-offer/page.tsx index 01cb9b7..b942bcd 100644 --- a/app/(public)/company-offer/page.tsx +++ b/app/(public)/company-offer/page.tsx @@ -1,9 +1,10 @@ import React from 'react'; import { MedReportLogo } from '@kit/shared/components/med-report-logo'; -import { withI18n } from '~/lib/i18n/with-i18n'; import { Trans } from '@kit/ui/trans'; +import { withI18n } from '~/lib/i18n/with-i18n'; + import CompanyOfferForm from './_components/company-offer-form'; function CompanyOffer() { diff --git a/app/(public)/layout.tsx b/app/(public)/layout.tsx index b897a04..bbb9b6d 100644 --- a/app/(public)/layout.tsx +++ b/app/(public)/layout.tsx @@ -1,9 +1,8 @@ import { withI18n } from '~/lib/i18n/with-i18n'; - function SiteLayout(props: React.PropsWithChildren) { return ( -
+
{props.children}
); diff --git a/app/admin/_components/admin-menu-navigation.tsx b/app/admin/_components/admin-menu-navigation.tsx new file mode 100644 index 0000000..7a6185d --- /dev/null +++ b/app/admin/_components/admin-menu-navigation.tsx @@ -0,0 +1,27 @@ +import { AppLogo } from '@kit/shared/components/app-logo'; +import { ProfileAccountDropdownContainer } from '@kit/shared/components/personal-account-dropdown-container'; +import { SIDEBAR_WIDTH_PROPERTY } from '@/packages/ui/src/shadcn/constants'; + +import type { UserWorkspace } from '../../home/(user)/_lib/server/load-user-workspace'; + +export function AdminMenuNavigation(props: { + workspace: UserWorkspace; +}) { + const { accounts } = props.workspace; + + return ( +
+
+ +
+ +
+
+ +
+
+
+ ); +} diff --git a/app/admin/_components/admin-sidebar.tsx b/app/admin/_components/admin-sidebar.tsx index 0492b98..20e6082 100644 --- a/app/admin/_components/admin-sidebar.tsx +++ b/app/admin/_components/admin-sidebar.tsx @@ -1,73 +1,25 @@ -'use client'; - -import Link from 'next/link'; -import { usePathname } from 'next/navigation'; - -import { UserWorkspace } from '@/app/home/(user)/_lib/server/load-user-workspace'; -import { LayoutDashboard, Users } from 'lucide-react'; - +import { adminNavigationConfig } from '@kit/shared/config'; import { Sidebar, SidebarContent, - SidebarFooter, - SidebarGroup, - SidebarGroupContent, - SidebarGroupLabel, SidebarHeader, - SidebarMenu, - SidebarMenuButton, - useSidebar, + SidebarNavigation, } from '@kit/ui/shadcn-sidebar'; -import { AppLogo } from '@kit/shared/components/app-logo'; -import { ProfileAccountDropdownContainer } from '@kit/shared/components/personal-account-dropdown-container'; +export function AdminSidebar() { + const collapsible = adminNavigationConfig.sidebarCollapsedStyle; -export function AdminSidebar({ - accounts, -}: { - accounts: UserWorkspace['accounts']; -}) { - const path = usePathname(); - const { open } = useSidebar(); return ( - - - + + +
+
Superadmin
+
- - Super Admin - - - - - - - Dashboard - - - - - - - Accounts - - - - - + - - - -
); } diff --git a/app/admin/accounts/[id]/page.tsx b/app/admin/accounts/[id]/page.tsx index 6f17480..f386f17 100644 --- a/app/admin/accounts/[id]/page.tsx +++ b/app/admin/accounts/[id]/page.tsx @@ -2,7 +2,9 @@ import { cache } from 'react'; import { AdminAccountPage } from '@kit/admin/components/admin-account-page'; import { AdminGuard } from '@kit/admin/components/admin-guard'; + import { getAccount } from '~/lib/services/account.service'; +import { withI18n } from '~/lib/i18n/with-i18n'; interface Params { params: Promise<{ @@ -26,6 +28,6 @@ async function AccountPage(props: Params) { return ; } -export default AdminGuard(AccountPage); +export default withI18n(AdminGuard(AccountPage)); const loadAccount = cache(getAccount); diff --git a/app/admin/accounts/page.tsx b/app/admin/accounts/page.tsx index 54cb3c6..93e1ffd 100644 --- a/app/admin/accounts/page.tsx +++ b/app/admin/accounts/page.tsx @@ -8,6 +8,7 @@ import { getSupabaseServerClient } from '@kit/supabase/server-client'; import { AppBreadcrumbs } from '@kit/ui/app-breadcrumbs'; import { Button } from '@kit/ui/button'; import { PageBody, PageHeader } from '@kit/ui/page'; +import { withI18n } from '~/lib/i18n/with-i18n'; interface SearchParams { page?: string; @@ -30,7 +31,7 @@ async function AccountsPage(props: AdminAccountsPageProps) { return ( <> - }> + } title="Accounts">
{promotions.length > 0 && ( -
-
+
+

@@ -111,32 +118,32 @@ export default function DiscountCode({ cart }: { return (
- + {promotion.code} - {" "} + {' '} ( {promotion.application_method?.value !== undefined && promotion.application_method.currency_code !== - undefined && ( + undefined && ( <> - {promotion.application_method.type === - "percentage" + {promotion.application_method.type === 'percentage' ? `${promotion.application_method.value}%` : convertToLocale({ - amount: Number(promotion.application_method.value), - currency_code: - promotion.application_method - .currency_code, - })} + amount: Number( + promotion.application_method.value, + ), + currency_code: + promotion.application_method.currency_code, + })} )} ) @@ -152,10 +159,10 @@ export default function DiscountCode({ cart }: { className="flex items-center" onClick={() => { if (!promotion.code) { - return + return; } - removePromotionCode(promotion.code) + removePromotionCode(promotion.code); }} data-testid="remove-discount-button" > @@ -166,11 +173,11 @@ export default function DiscountCode({ cart }: { )}
- ) + ); })}
)}
- ) + ); } diff --git a/app/home/(user)/_components/cart/index.tsx b/app/home/(user)/_components/cart/index.tsx index 09064bd..7887040 100644 --- a/app/home/(user)/_components/cart/index.tsx +++ b/app/home/(user)/_components/cart/index.tsx @@ -1,22 +1,21 @@ -"use client"; +'use client'; + +import { useState } from 'react'; + +import { handleNavigateToPayment } from '@/lib/services/medusaCart.service'; +import { formatCurrency } from '@/packages/shared/src/utils'; +import { initiatePaymentSession } from '@lib/data/cart'; +import { StoreCart, StoreCartLineItem } from '@medusajs/types'; +import { Loader2 } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; -import { useState } from "react"; -import { Loader2 } from "lucide-react"; -import { StoreCart, StoreCartLineItem } from "@medusajs/types" -import CartItems from "./cart-items" -import { Trans } from '@kit/ui/trans'; import { Button } from '@kit/ui/button'; -import { - Card, - CardContent, - CardHeader, -} from '@kit/ui/card'; -import DiscountCode from "./discount-code"; -import { initiatePaymentSession } from "@lib/data/cart"; -import { formatCurrency } from "@/packages/shared/src/utils"; -import { useTranslation } from "react-i18next"; -import { handleNavigateToPayment } from "@/lib/services/medusaCart.service"; -import AnalysisLocation from "./analysis-location"; +import { Card, CardContent, CardHeader } from '@kit/ui/card'; +import { Trans } from '@kit/ui/trans'; + +import AnalysisLocation from './analysis-location'; +import CartItems from './cart-items'; +import DiscountCode from './discount-code'; const IS_DISCOUNT_SHOWN = true as boolean; @@ -25,11 +24,13 @@ export default function Cart({ synlabAnalyses, ttoServiceItems, }: { - cart: StoreCart | null + cart: StoreCart | null; synlabAnalyses: StoreCartLineItem[]; ttoServiceItems: StoreCartLineItem[]; }) { - const { i18n: { language } } = useTranslation(); + const { + i18n: { language }, + } = useTranslation(); const [isInitiatingSession, setIsInitiatingSession] = useState(false); @@ -39,7 +40,10 @@ export default function Cart({ return (
-
+

@@ -71,21 +75,29 @@ export default function Cart({ const isLocationsShown = synlabAnalyses.length > 0; return ( -
-
- - +
+
+ +
{hasCartItems && ( <> -
-
-

+

+
+

-

+

{formatCurrency({ value: cart.subtotal, currencyCode: cart.currency_code, @@ -94,14 +106,14 @@ export default function Cart({

-
-
-

+

+
+

-

+

{formatCurrency({ value: cart.discount_total, currencyCode: cart.currency_code, @@ -110,14 +122,14 @@ export default function Cart({

-
-
-

+

+
+

-

+

{formatCurrency({ value: cart.total, currencyCode: cart.currency_code, @@ -129,11 +141,9 @@ export default function Cart({ )} -

+
{IS_DISCOUNT_SHOWN && ( - +
@@ -146,24 +156,31 @@ export default function Cart({ )} {isLocationsShown && ( - +
- +
)}
-
diff --git a/app/home/(user)/_components/cart/partner-locations.json b/app/home/(user)/_components/cart/partner-locations.json index 1266f0c..147059b 100644 --- a/app/home/(user)/_components/cart/partner-locations.json +++ b/app/home/(user)/_components/cart/partner-locations.json @@ -119,4 +119,4 @@ "hours": "Verevõtt tööpäeviti 8.00-12.00", "city": "Otepää" } -] \ No newline at end of file +] diff --git a/app/home/(user)/_components/cart/types.ts b/app/home/(user)/_components/cart/types.ts index 22385ce..9150557 100644 --- a/app/home/(user)/_components/cart/types.ts +++ b/app/home/(user)/_components/cart/types.ts @@ -4,12 +4,12 @@ export interface MontonioOrderToken { merchantReference: string; merchantReferenceDisplay: string; paymentStatus: - | 'PAID' - | 'FAILED' - | 'CANCELLED' - | 'PENDING' - | 'EXPIRED' - | 'REFUNDED'; + | 'PAID' + | 'FAILED' + | 'CANCELLED' + | 'PENDING' + | 'EXPIRED' + | 'REFUNDED'; paymentMethod: string; grandTotal: number; currency: string; @@ -19,4 +19,4 @@ export interface MontonioOrderToken { paymentLinkUuid: string; iat: number; exp: number; -} \ No newline at end of file +} diff --git a/app/home/(user)/_components/compare-packages-modal.tsx b/app/home/(user)/_components/compare-packages-modal.tsx index eadfd88..06797b4 100644 --- a/app/home/(user)/_components/compare-packages-modal.tsx +++ b/app/home/(user)/_components/compare-packages-modal.tsx @@ -1,9 +1,13 @@ import { JSX } from 'react'; +import { StoreProduct } from '@medusajs/types'; import { QuestionMarkCircledIcon } from '@radix-ui/react-icons'; import { VisuallyHidden } from '@radix-ui/react-visually-hidden'; import { Check, X } from 'lucide-react'; +import { PackageHeader } from '@kit/shared/components/package-header'; +import { AnalysisPackageWithVariant } from '@kit/shared/components/select-analysis-package'; +import { InfoTooltip } from '@kit/shared/components/ui/info-tooltip'; import { Dialog, DialogContent, @@ -18,14 +22,14 @@ import { TableHeader, TableRow, } from '@kit/ui/table'; + import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; -import { PackageHeader } from '@kit/shared/components/package-header'; -import { InfoTooltip } from '@kit/shared/components/ui/info-tooltip'; -import { StoreProduct } from '@medusajs/types'; -import { AnalysisPackageWithVariant } from '@kit/shared/components/select-analysis-package'; import { withI18n } from '~/lib/i18n/with-i18n'; -export type AnalysisPackageElement = Pick & { +export type AnalysisPackageElement = Pick< + StoreProduct, + 'title' | 'id' | 'description' +> & { isIncludedInStandard: boolean; isIncludedInStandardPlus: boolean; isIncludedInPremium: boolean; @@ -39,7 +43,11 @@ const CheckWithBackground = () => { ); }; -const PackageTableHead = async ({ product }: { product: AnalysisPackageWithVariant }) => { +const PackageTableHead = async ({ + product, +}: { + product: AnalysisPackageWithVariant; +}) => { const { t, language } = await createI18nServerInstance(); const { title, price, nrOfAnalyses } = product; @@ -48,14 +56,14 @@ const PackageTableHead = async ({ product }: { product: AnalysisPackageWithVaria - ) -} + ); +}; const ComparePackagesModal = async ({ analysisPackages, @@ -69,7 +77,9 @@ const ComparePackagesModal = async ({ const { t } = await createI18nServerInstance(); const standardPackage = analysisPackages.find(({ isStandard }) => isStandard); - const standardPlusPackage = analysisPackages.find(({ isStandardPlus }) => isStandardPlus); + const standardPlusPackage = analysisPackages.find( + ({ isStandardPlus }) => isStandardPlus, + ); const premiumPackage = analysisPackages.find(({ isPremium }) => isPremium); if (!standardPackage || !standardPlusPackage || !premiumPackage) { @@ -100,7 +110,7 @@ const ComparePackagesModal = async ({

{t('product:healthPackageComparison.description')}

-
+
@@ -112,16 +122,14 @@ const ComparePackagesModal = async ({ {analysisPackageElements.map( - ( - { - title, - id, - description, - isIncludedInStandard, - isIncludedInStandardPlus, - isIncludedInPremium, - }, - ) => { + ({ + title, + id, + description, + isIncludedInStandard, + isIncludedInStandardPlus, + isIncludedInPremium, + }) => { if (!title) { return null; } @@ -130,20 +138,28 @@ const ComparePackagesModal = async ({ {title}{' '} - {description && (} />)} + {description && ( + } + /> + )} {isIncludedInStandard && } - {isIncludedInStandardPlus && } + {isIncludedInStandardPlus && ( + + )} {isIncludedInPremium && } ); - })} + }, + )}
diff --git a/app/home/(user)/_components/dashboard-cards.tsx b/app/home/(user)/_components/dashboard-cards.tsx index 3127331..686afe9 100644 --- a/app/home/(user)/_components/dashboard-cards.tsx +++ b/app/home/(user)/_components/dashboard-cards.tsx @@ -11,7 +11,7 @@ export default function DashboardCards() {
{ @@ -57,80 +58,78 @@ const cards = ({ bmiStatus: BmiCategory | null; smoking?: boolean | null; }) => [ - { - title: 'dashboard:gender', - description: gender ?? '-', - icon: , - iconBg: 'bg-success', - }, - { - title: 'dashboard:age', - description: age ? `${age}` : '-', - icon: , - iconBg: 'bg-success', - }, - { - title: 'dashboard:height', - description: height ? `${height}cm` : '-', - icon: , - iconBg: 'bg-success', - }, - { - title: 'dashboard:weight', - description: weight ? `${weight}kg` : '-', - icon: , - iconBg: 'bg-success', - }, - { - title: 'dashboard:bmi', - description: bmiFromMetric(weight || 0, height || 0)?.toString() ?? '-', - icon: , - iconBg: getBmiBackgroundColor(bmiStatus), - }, - { - title: 'dashboard:bloodPressure', - description: '-', - icon: , - iconBg: 'bg-warning', - }, - { - title: 'dashboard:cholesterol', - description: '-', - icon: , - iconBg: 'bg-destructive', - }, - { - title: 'dashboard:ldlCholesterol', - description: '-', - icon: , - iconBg: 'bg-warning', - }, - // { - // title: 'Score 2', - // description: 'Normis', - // icon: , - // iconBg: 'bg-success', - // }, - { - title: 'dashboard:smoking', - description: - isNil(smoking) - ? 'dashboard:respondToQuestion' - : !!smoking - ? 'common:yes' - : 'common:no', - descriptionColor: 'text-primary', - icon: - isNil(smoking) ? ( - - - - ) : null, - cardVariant: getCardVariant(isNil(smoking) ? null : !smoking), - }, - ]; + { + title: 'dashboard:gender', + description: gender ?? '-', + icon: , + iconBg: 'bg-success', + }, + { + title: 'dashboard:age', + description: age ? `${age}` : '-', + icon: , + iconBg: 'bg-success', + }, + { + title: 'dashboard:height', + description: height ? `${height}cm` : '-', + icon: , + iconBg: 'bg-success', + }, + { + title: 'dashboard:weight', + description: weight ? `${weight}kg` : '-', + icon: , + iconBg: 'bg-success', + }, + { + title: 'dashboard:bmi', + description: bmiFromMetric(weight || 0, height || 0)?.toString() ?? '-', + icon: , + iconBg: getBmiBackgroundColor(bmiStatus), + }, + { + title: 'dashboard:bloodPressure', + description: '-', + icon: , + iconBg: 'bg-warning', + }, + { + title: 'dashboard:cholesterol', + description: '-', + icon: , + iconBg: 'bg-destructive', + }, + { + title: 'dashboard:ldlCholesterol', + description: '-', + icon: , + iconBg: 'bg-warning', + }, + // { + // title: 'Score 2', + // description: 'Normis', + // icon: , + // iconBg: 'bg-success', + // }, + { + title: 'dashboard:smoking', + description: isNil(smoking) + ? 'dashboard:respondToQuestion' + : smoking + ? 'common:yes' + : 'common:no', + descriptionColor: 'text-primary', + icon: isNil(smoking) ? ( + + + + ) : null, + cardVariant: getCardVariant(isNil(smoking) ? null : !smoking), + }, +]; const IS_SHOWN_RECOMMENDATIONS = false as boolean; @@ -146,13 +145,15 @@ export default function Dashboard({ }) { const height = account.accountParams?.height || 0; const weight = account.accountParams?.weight || 0; - + let age: number = 0; let gender: { label: string; value: string } | null = null; try { - ({ age = 0, gender } = PersonalCode.parsePersonalCode(account.personal_code!)); + ({ age = 0, gender } = PersonalCode.parsePersonalCode( + account.personal_code!, + )); } catch (e) { - console.error("Failed to parse personal code", e); + console.error('Failed to parse personal code', e); } const bmiStatus = getBmiStatus(bmiThresholds, { age, height, weight }); diff --git a/app/home/(user)/_components/home-account-selector.tsx b/app/home/(user)/_components/home-account-selector.tsx index 5ea17c6..813e45c 100644 --- a/app/home/(user)/_components/home-account-selector.tsx +++ b/app/home/(user)/_components/home-account-selector.tsx @@ -5,11 +5,9 @@ import { useContext } from 'react'; import { useRouter } from 'next/navigation'; import { AccountSelector } from '@kit/accounts/account-selector'; +import { featureFlagsConfig, pathsConfig } from '@kit/shared/config'; import { SidebarContext } from '@kit/ui/shadcn-sidebar'; -import { pathsConfig, featureFlagsConfig } from '@kit/shared/config'; - - const features = { enableTeamCreation: featureFlagsConfig.enableTeamCreation, }; diff --git a/app/home/(user)/_components/home-page-header.tsx b/app/home/(user)/_components/home-page-header.tsx index da64f7f..8beb78f 100644 --- a/app/home/(user)/_components/home-page-header.tsx +++ b/app/home/(user)/_components/home-page-header.tsx @@ -7,6 +7,8 @@ export function HomeLayoutPageHeader( }>, ) { return ( - {props.children} + + {props.children} + ); } diff --git a/app/home/(user)/_components/order-analyses-cards.tsx b/app/home/(user)/_components/order-analyses-cards.tsx index bd7f8a7..8d6a04d 100644 --- a/app/home/(user)/_components/order-analyses-cards.tsx +++ b/app/home/(user)/_components/order-analyses-cards.tsx @@ -1,25 +1,23 @@ -"use client"; +'use client'; +import { useState } from 'react'; + +import { formatCurrency } from '@/packages/shared/src/utils'; +import { StoreProduct } from '@medusajs/types'; import { HeartPulse, Loader2, ShoppingCart } from 'lucide-react'; import { useTranslation } from 'react-i18next'; -import { Button } from '@kit/ui/button'; -import { - Card, - CardHeader, - CardFooter, - CardDescription, -} from '@kit/ui/card'; -import { StoreProduct } from '@medusajs/types'; -import { useState } from 'react'; -import { handleAddToCart } from '~/lib/services/medusaCart.service'; import { InfoTooltip } from '@kit/shared/components/ui/info-tooltip'; -import { Trans } from '@kit/ui/trans'; +import { Button } from '@kit/ui/button'; +import { Card, CardDescription, CardFooter, CardHeader } from '@kit/ui/card'; import { toast } from '@kit/ui/sonner'; -import { formatCurrency } from '@/packages/shared/src/utils'; +import { Trans } from '@kit/ui/trans'; + +import { handleAddToCart } from '~/lib/services/medusaCart.service'; export type OrderAnalysisCard = Pick< - StoreProduct, 'title' | 'description' | 'subtitle' + StoreProduct, + 'title' | 'description' | 'subtitle' > & { variant: { id: string }; price: number | null; @@ -32,10 +30,13 @@ export default function OrderAnalysesCards({ analyses: OrderAnalysisCard[]; countryCode: string; }) { + const { + i18n: { language }, + } = useTranslation(); - const { i18n: { language } } = useTranslation() - - const [variantAddingToCart, setVariantAddingToCart] = useState(null); + const [variantAddingToCart, setVariantAddingToCart] = useState( + null, + ); const handleSelect = async (variantId: string) => { if (variantAddingToCart) { return null; @@ -54,24 +55,19 @@ export default function OrderAnalysesCards({ setVariantAddingToCart(null); console.error(e); } - } + }; return ( -
- {analyses.map(({ - title, - variant, - description, - subtitle, - price, - }) => { - const formattedPrice = typeof price === 'number' - ? formatCurrency({ - currencyCode: 'eur', - locale: language, - value: price, - }) - : null; +
+ {analyses.map(({ title, variant, description, subtitle, price }) => { + const formattedPrice = + typeof price === 'number' + ? formatCurrency({ + currencyCode: 'eur', + locale: language, + value: price, + }) + : null; return (
-
+
-
+
{title} {description && ( @@ -104,7 +106,7 @@ export default function OrderAnalysesCards({ {' '} +
{formattedPrice} {description}
@@ -113,11 +115,7 @@ export default function OrderAnalysesCards({ )}
- {subtitle && ( - - {subtitle} - - )} + {subtitle && {subtitle}}
{formattedPrice} diff --git a/app/home/(user)/_components/order/cart-totals.tsx b/app/home/(user)/_components/order/cart-totals.tsx index fb7b030..bc59e8f 100644 --- a/app/home/(user)/_components/order/cart-totals.tsx +++ b/app/home/(user)/_components/order/cart-totals.tsx @@ -1,15 +1,21 @@ -"use client" +'use client'; + +import React from 'react'; + +import { formatCurrency } from '@/packages/shared/src/utils'; +import { StoreOrder } from '@medusajs/types'; +import { useTranslation } from 'react-i18next'; -import { formatCurrency } from "@/packages/shared/src/utils" -import { StoreOrder } from "@medusajs/types" -import React from "react" -import { useTranslation } from "react-i18next" import { Trans } from '@kit/ui/trans'; -export default function CartTotals({ medusaOrder }: { - medusaOrder: StoreOrder +export default function CartTotals({ + medusaOrder, +}: { + medusaOrder: StoreOrder; }) { - const { i18n: { language } } = useTranslation() + const { + i18n: { language }, + } = useTranslation(); const { currency_code, total, @@ -17,29 +23,39 @@ export default function CartTotals({ medusaOrder }: { tax_total, discount_total, gift_card_total, - } = medusaOrder + } = medusaOrder; return (
-
+
- + - {formatCurrency({ value: subtotal ?? 0, currencyCode: currency_code, locale: language })} + {formatCurrency({ + value: subtotal ?? 0, + currencyCode: currency_code, + locale: language, + })}
{!!discount_total && (
- + + + - -{" "} - {formatCurrency({ value: discount_total ?? 0, currencyCode: currency_code, locale: language })} + -{' '} + {formatCurrency({ + value: discount_total ?? 0, + currencyCode: currency_code, + locale: language, + })}
)} @@ -53,30 +69,42 @@ export default function CartTotals({ medusaOrder }: {
*/} {!!gift_card_total && (
- + + + - -{" "} - {formatCurrency({ value: gift_card_total ?? 0, currencyCode: currency_code, locale: language })} + -{' '} + {formatCurrency({ + value: gift_card_total ?? 0, + currencyCode: currency_code, + locale: language, + })}
)}
-
-
- +
+
+ + + - {formatCurrency({ value: total ?? 0, currencyCode: currency_code, locale: language })} + {formatCurrency({ + value: total ?? 0, + currencyCode: currency_code, + locale: language, + })}
-
+
- ) + ); } diff --git a/app/home/(user)/_components/order/order-details.tsx b/app/home/(user)/_components/order/order-details.tsx index e10f3bc..1cda501 100644 --- a/app/home/(user)/_components/order/order-details.tsx +++ b/app/home/(user)/_components/order/order-details.tsx @@ -1,29 +1,25 @@ -import { Trans } from '@kit/ui/trans'; import { formatDate } from 'date-fns'; -import type { AnalysisOrder } from "~/lib/types/analysis-order"; -export default function OrderDetails({ order }: { - order: AnalysisOrder -}) { +import { Trans } from '@kit/ui/trans'; + +import type { AnalysisOrder } from '~/lib/types/analysis-order'; + +export default function OrderDetails({ order }: { order: AnalysisOrder }) { return (
- :{" "} - - - {order.medusa_order_id} + :{' '} + {order.medusa_order_id}
- :{" "} - - - {formatDate(order.created_at, 'dd.MM.yyyy HH:mm')} + :{' '} + {formatDate(order.created_at, 'dd.MM.yyyy HH:mm')}
- ) + ); } diff --git a/app/home/(user)/_components/order/order-item.tsx b/app/home/(user)/_components/order/order-item.tsx index cad98e6..ed9ee37 100644 --- a/app/home/(user)/_components/order/order-item.tsx +++ b/app/home/(user)/_components/order/order-item.tsx @@ -1,13 +1,16 @@ -import { StoreCartLineItem, StoreOrderLineItem } from "@medusajs/types" -import { TableCell, TableRow } from "@kit/ui/table" - +import { StoreCartLineItem, StoreOrderLineItem } from '@medusajs/types'; // import LineItemOptions from "@modules/common/components/line-item-options" -import LineItemPrice from "@modules/common/components/line-item-price" -import LineItemUnitPrice from "@modules/common/components/line-item-unit-price" +import LineItemPrice from '@modules/common/components/line-item-price'; +import LineItemUnitPrice from '@modules/common/components/line-item-unit-price'; -export default function OrderItem({ item, currencyCode }: { - item: StoreCartLineItem | StoreOrderLineItem - currencyCode: string +import { TableCell, TableRow } from '@kit/ui/table'; + +export default function OrderItem({ + item, + currencyCode, +}: { + item: StoreCartLineItem | StoreOrderLineItem; + currencyCode: string; }) { const partnerLocationName = item.metadata?.partner_location_name; return ( @@ -18,22 +21,21 @@ export default function OrderItem({ item, currencyCode }: {
*/} - + - {item.product_title}{` ${partnerLocationName ? `(${partnerLocationName})` : ''}`} + {item.product_title} + {` ${partnerLocationName ? `(${partnerLocationName})` : ''}`} {/* */} - - - - {item.quantity}x{" "} - + + + {item.quantity}x - ) + ); } diff --git a/app/home/(user)/_components/order/order-items.tsx b/app/home/(user)/_components/order/order-items.tsx index 5375314..98b18d1 100644 --- a/app/home/(user)/_components/order/order-items.tsx +++ b/app/home/(user)/_components/order/order-items.tsx @@ -1,39 +1,44 @@ -import repeat from "@lib/util/repeat" -import { StoreOrder } from "@medusajs/types" -import { Table, TableBody } from "@kit/ui/table" +import repeat from '@lib/util/repeat'; +import { StoreOrder } from '@medusajs/types'; +import SkeletonLineItem from '@modules/skeletons/components/skeleton-line-item'; -import SkeletonLineItem from "@modules/skeletons/components/skeleton-line-item" -import OrderItem from "./order-item" -import { Heading } from "@kit/ui/heading" +import { Heading } from '@kit/ui/heading'; +import { Table, TableBody } from '@kit/ui/table'; import { Trans } from '@kit/ui/trans'; -export default function OrderItems({ medusaOrder }: { - medusaOrder: StoreOrder +import OrderItem from './order-item'; + +export default function OrderItems({ + medusaOrder, +}: { + medusaOrder: StoreOrder; }) { - const items = medusaOrder.items + const items = medusaOrder.items; return (
- +
- +
{items?.length ? items - .sort((a, b) => (a.created_at ?? "") > (b.created_at ?? "") ? -1 : 1) - .map((item) => ( - - )) + .sort((a, b) => + (a.created_at ?? '') > (b.created_at ?? '') ? -1 : 1, + ) + .map((item) => ( + + )) : repeat(5).map((i) => )}
- ) + ); } diff --git a/app/home/(user)/_components/orders/actions.ts b/app/home/(user)/_components/orders/actions.ts index 07bdfbc..91fb129 100644 --- a/app/home/(user)/_components/orders/actions.ts +++ b/app/home/(user)/_components/orders/actions.ts @@ -1,9 +1,15 @@ 'use server'; -import { createPageViewLog, PageViewAction } from "~/lib/services/audit/pageView.service"; -import { loadCurrentUserAccount } from "../../_lib/server/load-user-account"; +import { + PageViewAction, + createPageViewLog, +} from '~/lib/services/audit/pageView.service'; -export async function logAnalysisResultsNavigateAction(analysisOrderId: string) { +import { loadCurrentUserAccount } from '../../_lib/server/load-user-account'; + +export async function logAnalysisResultsNavigateAction( + analysisOrderId: string, +) { const { account } = await loadCurrentUserAccount(); if (!account) { throw new Error('Account not found'); diff --git a/app/home/(user)/_components/orders/order-block.tsx b/app/home/(user)/_components/orders/order-block.tsx index ccffd9e..03f0447 100644 --- a/app/home/(user)/_components/orders/order-block.tsx +++ b/app/home/(user)/_components/orders/order-block.tsx @@ -1,37 +1,57 @@ -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"; -import Link from "next/link"; -import { Eye } from "lucide-react"; +import Link from 'next/link'; -export default function OrderBlock({ analysisOrder, itemsAnalysisPackage, itemsOther }: { - analysisOrder: AnalysisOrder, - itemsAnalysisPackage: StoreOrderLineItem[], - itemsOther: StoreOrderLineItem[], +import { StoreOrderLineItem } from '@medusajs/types'; +import { Eye } from 'lucide-react'; + +import { Trans } from '@kit/ui/makerkit/trans'; + +import type { AnalysisOrder } from '~/lib/types/analysis-order'; + +import OrderItemsTable from './order-items-table'; + +export default function OrderBlock({ + analysisOrder, + itemsAnalysisPackage, + itemsOther, +}: { + analysisOrder: AnalysisOrder; + itemsAnalysisPackage: StoreOrderLineItem[]; + itemsOther: StoreOrderLineItem[]; }) { return (

- + {` (${analysisOrder.id})`}

- -
- - + +
- ) + ); } diff --git a/app/home/(user)/_components/orders/order-items-table.tsx b/app/home/(user)/_components/orders/order-items-table.tsx index fc27d81..8511c08 100644 --- a/app/home/(user)/_components/orders/order-items-table.tsx +++ b/app/home/(user)/_components/orders/order-items-table.tsx @@ -65,7 +65,7 @@ export default function OrderItemsTable({ ) .map((orderItem) => ( - +

{orderItem.product_title}

diff --git a/app/home/(user)/_components/recommendations-skeleton.tsx b/app/home/(user)/_components/recommendations-skeleton.tsx new file mode 100644 index 0000000..eaa9917 --- /dev/null +++ b/app/home/(user)/_components/recommendations-skeleton.tsx @@ -0,0 +1,75 @@ +import React from 'react'; + +import { InfoTooltip } from '@/packages/shared/src/components/ui/info-tooltip'; +import { HeartPulse } from 'lucide-react'; + +import { Button } from '@kit/ui/shadcn/button'; +import { + Card, + CardDescription, + CardFooter, + CardHeader, +} from '@kit/ui/shadcn/card'; +import { Skeleton } from '@kit/ui/skeleton'; + +const RecommendationsSkeleton = () => { + const emptyData = [ + { + title: '1', + description: '', + subtitle: '', + variant: { id: '' }, + price: 1, + }, + { + title: '2', + description: '', + subtitle: '', + variant: { id: '' }, + price: 1, + }, + ]; + return ( +
+ {emptyData.map(({ title, description, subtitle }) => ( + + + +
+
+
+ + +
+
+ {title} + {description && ( + <> + {' '} + + {description} +
+ } + /> + + )} + + {subtitle && {subtitle}} +
+
+ +
+
+ ))} +
+ ); +}; + +export default RecommendationsSkeleton; diff --git a/app/home/(user)/_components/recommendations.tsx b/app/home/(user)/_components/recommendations.tsx new file mode 100644 index 0000000..71403ef --- /dev/null +++ b/app/home/(user)/_components/recommendations.tsx @@ -0,0 +1,30 @@ +'use server'; + +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'; + +export default async function Recommendations({ + account, +}: { + account: AccountWithParams; +}) { + const { analyses, countryCode } = await loadAnalyses(); + + const analysisRecommendations = await loadRecommendations(analyses, account); + const orderAnalyses = analyses.filter((analysis) => + analysisRecommendations.includes(analysis.title), + ); + + if (orderAnalyses.length === 0) { + return null; + } + + return ( + + ); +} diff --git a/app/home/(user)/_lib/server/load-accounts.ts b/app/home/(user)/_lib/server/load-accounts.ts index 77e7e42..e48e831 100644 --- a/app/home/(user)/_lib/server/load-accounts.ts +++ b/app/home/(user)/_lib/server/load-accounts.ts @@ -1,6 +1,5 @@ import { cache } from 'react'; - import { createAccountsApi } from '@kit/accounts/api'; import { getSupabaseServerClient } from '@kit/supabase/server-client'; diff --git a/app/home/(user)/_lib/server/load-analyses.ts b/app/home/(user)/_lib/server/load-analyses.ts index cd16e61..411d68a 100644 --- a/app/home/(user)/_lib/server/load-analyses.ts +++ b/app/home/(user)/_lib/server/load-analyses.ts @@ -1,7 +1,7 @@ import { cache } from 'react'; import { getProductCategories } from '@lib/data/categories'; -import { listProducts, listProductTypes } from '@lib/data/products'; +import { listProductTypes, listProducts } from '@lib/data/products'; import { listRegions } from '@lib/data/regions'; import { OrderAnalysisCard } from '../../_components/order-analyses-cards'; @@ -40,9 +40,9 @@ async function analysesLoader() { ); const categoryProducts = category ? await listProducts({ - countryCode, - queryParams: { limit: 100, category_id: category.id, order: 'title' }, - }) + countryCode, + queryParams: { limit: 100, category_id: category.id, order: 'title' }, + }) : null; const serviceCategories = productCategories.filter( @@ -52,21 +52,24 @@ async function analysesLoader() { return { analyses: categoryProducts?.response.products - .filter(({ status, metadata }) => status === 'published' && !!metadata?.analysisIdOriginal) - .map( - ({ title, description, subtitle, variants }) => { - const variant = variants![0]!; - return { - title, - description, - subtitle, - variant: { - id: variant.id, - }, - price: variant.calculated_price?.calculated_amount ?? null, - }; - }, - ) ?? [], + .filter( + ({ status, metadata }) => + status === 'published' && !!metadata?.analysisIdOriginal, + ) + .map( + ({ title, description, subtitle, variants }) => { + const variant = variants![0]!; + return { + title, + description, + subtitle, + variant: { + id: variant.id, + }, + price: variant.calculated_price?.calculated_amount ?? null, + }; + }, + ) ?? [], countryCode, }; } diff --git a/app/home/(user)/_lib/server/load-analysis-packages.ts b/app/home/(user)/_lib/server/load-analysis-packages.ts index 596a041..f564e08 100644 --- a/app/home/(user)/_lib/server/load-analysis-packages.ts +++ b/app/home/(user)/_lib/server/load-analysis-packages.ts @@ -1,14 +1,17 @@ import { cache } from 'react'; -import { listProductTypes, listProducts } from "@lib/data/products"; -import { listRegions } from '@lib/data/regions'; import { getAnalysisElementMedusaProductIds } from '@/utils/medusa-product'; +import { listProductTypes, listProducts } from '@lib/data/products'; +import { listRegions } from '@lib/data/regions'; import type { StoreProduct } from '@medusajs/types'; -import { loadCurrentUserAccount } from './load-user-account'; + import type { AccountWithParams } from '@kit/accounts/types/accounts'; import type { AnalysisPackageWithVariant } from '@kit/shared/components/select-analysis-package'; + import PersonalCode from '~/lib/utils'; +import { loadCurrentUserAccount } from './load-user-account'; + async function countryCodesLoader() { const countryCodes = await listRegions().then((regions) => regions?.map((r) => r.countries?.map((c) => c.iso_2)).flat(), @@ -33,24 +36,25 @@ function userSpecificVariantLoader({ throw new Error('Personal code not found'); } - const { ageRange, gender: { value: gender } } = PersonalCode.parsePersonalCode(personalCode); + const { + ageRange, + gender: { value: gender }, + } = PersonalCode.parsePersonalCode(personalCode); - return ({ - product, - }: { - product: StoreProduct; - }) => { + return ({ product }: { product: StoreProduct }) => { const variants = product.variants; if (!variants) { return null; } - const variant = variants.find((v) => v.options?.every((o) => [ageRange, gender].includes(o.value))); + const variant = variants.find((v) => + v.options?.every((o) => [ageRange, gender].includes(o.value)), + ); if (!variant) { return null; } return variant; - } + }; } async function analysisPackageElementsLoader({ @@ -60,30 +64,46 @@ async function analysisPackageElementsLoader({ analysisPackagesWithVariant: AnalysisPackageWithVariant[]; countryCode: string; }) { - const analysisElementMedusaProductIds = getAnalysisElementMedusaProductIds(analysisPackagesWithVariant); + const analysisElementMedusaProductIds = getAnalysisElementMedusaProductIds( + analysisPackagesWithVariant, + ); if (analysisElementMedusaProductIds.length === 0) { return []; } - const { response: { products } } = await listProducts({ + const { + response: { products }, + } = await listProducts({ countryCode, queryParams: { id: analysisElementMedusaProductIds, limit: 100, - order: "title", + order: 'title', }, }); - const standardPackage = analysisPackagesWithVariant.find(({ isStandard }) => isStandard); - const standardPlusPackage = analysisPackagesWithVariant.find(({ isStandardPlus }) => isStandardPlus); - const premiumPackage = analysisPackagesWithVariant.find(({ isPremium }) => isPremium); + const standardPackage = analysisPackagesWithVariant.find( + ({ isStandard }) => isStandard, + ); + const standardPlusPackage = analysisPackagesWithVariant.find( + ({ isStandardPlus }) => isStandardPlus, + ); + const premiumPackage = analysisPackagesWithVariant.find( + ({ isPremium }) => isPremium, + ); if (!standardPackage || !standardPlusPackage || !premiumPackage) { return []; } - const standardPackageAnalyses = getAnalysisElementMedusaProductIds([standardPackage]); - const standardPlusPackageAnalyses = getAnalysisElementMedusaProductIds([standardPlusPackage]); - const premiumPackageAnalyses = getAnalysisElementMedusaProductIds([premiumPackage]); + const standardPackageAnalyses = getAnalysisElementMedusaProductIds([ + standardPackage, + ]); + const standardPlusPackageAnalyses = getAnalysisElementMedusaProductIds([ + standardPlusPackage, + ]); + const premiumPackageAnalyses = getAnalysisElementMedusaProductIds([ + premiumPackage, + ]); return products.map(({ id, title, description }) => ({ id, @@ -103,18 +123,20 @@ async function analysisPackagesWithVariantLoader({ countryCode: string; }) { const productTypes = await loadProductTypes(); - const productType = productTypes.find(({ metadata }) => metadata?.handle === 'analysis-packages'); + const productType = productTypes.find( + ({ metadata }) => metadata?.handle === 'analysis-packages', + ); if (!productType) { return null; } const analysisPackagesResponse = await listProducts({ countryCode, - queryParams: { limit: 100, "type_id[0]": productType.id }, + queryParams: { limit: 100, 'type_id[0]': productType.id }, }); const getVariant = userSpecificVariantLoader({ account }); - const analysisPackagesWithVariant = analysisPackagesResponse.response.products - .reduce((acc, product) => { + const analysisPackagesWithVariant = + analysisPackagesResponse.response.products.reduce((acc, product) => { const variant = getVariant({ product }); if (!variant) { return acc; @@ -124,14 +146,17 @@ async function analysisPackagesWithVariantLoader({ { variant, variantId: variant.id, - nrOfAnalyses: getAnalysisElementMedusaProductIds([{ ...product, variant }]).length, + nrOfAnalyses: getAnalysisElementMedusaProductIds([ + { ...product, variant }, + ]).length, price: variant.calculated_price?.calculated_amount ?? 0, title: product.title, subtitle: product.subtitle, description: product.description, metadata: product.metadata, isStandard: product.metadata?.analysisPackageTier === 'standard', - isStandardPlus: product.metadata?.analysisPackageTier === 'standard-plus', + isStandardPlus: + product.metadata?.analysisPackageTier === 'standard-plus', isPremium: product.metadata?.analysisPackageTier === 'premium', }, ]; @@ -149,13 +174,23 @@ async function analysisPackagesLoader() { const countryCodes = await loadCountryCodes(); const countryCode = countryCodes[0]!; - const analysisPackagesWithVariant = await analysisPackagesWithVariantLoader({ account, countryCode }); + const analysisPackagesWithVariant = await analysisPackagesWithVariantLoader({ + account, + countryCode, + }); if (!analysisPackagesWithVariant) { return { analysisPackageElements: [], analysisPackages: [], countryCode }; } - const analysisPackageElements = await analysisPackageElementsLoader({ analysisPackagesWithVariant, countryCode }); + const analysisPackageElements = await analysisPackageElementsLoader({ + analysisPackagesWithVariant, + countryCode, + }); - return { analysisPackageElements, analysisPackages: analysisPackagesWithVariant, countryCode }; + return { + analysisPackageElements, + analysisPackages: analysisPackagesWithVariant, + countryCode, + }; } export const loadAnalysisPackages = cache(analysisPackagesLoader); 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..8763c87 --- /dev/null +++ b/app/home/(user)/_lib/server/load-recommendations.ts @@ -0,0 +1,140 @@ +import { cache } from 'react'; + +import { AccountWithParams } from '@/packages/features/accounts/src/types/accounts'; +import { createUserAnalysesApi } from '@/packages/features/user-analyses/src/server/api'; +import { getSupabaseServerClient } from '@/packages/supabase/src/clients/server-client'; +import { Database } from '@/packages/supabase/src/database.types'; +import OpenAI from 'openai'; + +import PersonalCode from '~/lib/utils'; + +import { OrderAnalysisCard } from '../../_components/order-analyses-cards'; + +export const loadRecommendations = cache(recommendationsLoader); + +type AnalysisResponses = + Database['medreport']['Functions']['get_latest_analysis_response_elements_for_current_user']['Returns']; + +const getLatestResponseTime = (items: AnalysisResponses) => { + if (!items?.length) return null; + + let latest = null; + for (const it of items) { + const d = new Date(it.response_time); + const t = d.getTime(); + if (!Number.isNaN(t) && (latest === null || t > latest.getTime())) { + latest = d; + } + } + return latest; +}; + +async function recommendationsLoader( + analyses: OrderAnalysisCard[], + account: AccountWithParams | null, +): Promise { + if (!process.env.OPENAI_API_KEY) { + return []; + } + if (!account?.personal_code) { + return []; + } + const supabaseClient = getSupabaseServerClient(); + const userAnalysesApi = createUserAnalysesApi(supabaseClient); + const analysisResponses = await userAnalysesApi.getAllUserAnalysisResponses(); + const analysesRecommendationsPromptId = + process.env.PROMPT_ID_ANALYSIS_RECOMMENDATIONS; + const latestResponseTime = getLatestResponseTime(analysisResponses); + const latestISO = latestResponseTime + ? new Date(latestResponseTime).toISOString() + : new Date('2025').toISOString(); + + if (!analysesRecommendationsPromptId) { + console.error('No prompt ID for analysis recommendations'); + return []; + } + + const previouslyRecommended = await supabaseClient + .schema('medreport') + .from('ai_responses') + .select('*') + .eq('account_id', account.id) + .eq('prompt_id', analysesRecommendationsPromptId) + .eq('latest_data_change', latestISO); + + if (previouslyRecommended.data?.[0]?.response) { + return JSON.parse(previouslyRecommended.data[0].response as string) + .recommended; + } + + const openAIClient = new OpenAI(); + const { gender, age } = PersonalCode.parsePersonalCode(account.personal_code); + const weight = account.accountParams?.weight || 'unknown'; + + const formattedAnalysisResponses = analysisResponses.map( + ({ + analysis_name_lab, + response_value, + norm_upper, + norm_lower, + norm_status, + }) => ({ + name: analysis_name_lab, + value: response_value, + normUpper: norm_upper, + normLower: norm_lower, + normStatus: norm_status, + }), + ); + const formattedAnalyses = analyses.map(({ description, title }) => ({ + description, + title, + })); + + let response; + + try { + 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(), + }, + }, + }); + } catch (error) { + console.error('Error calling OpenAI: ', error); + return []; + } + + const json = JSON.parse(response.output_text); + + try { + await supabaseClient + .schema('medreport') + .from('ai_responses') + .insert({ + account_id: account.id, + prompt_name: 'Analysis Recommendations', + prompt_id: analysesRecommendationsPromptId, + input: JSON.stringify({ + analyses: formattedAnalyses, + results: formattedAnalysisResponses, + gender, + age, + weight, + }), + latest_data_change: latestISO, + response: response.output_text, + }); + } catch (error) { + console.error('Error saving AI response: ', error); + } + + return json.recommended; +} diff --git a/app/home/(user)/_lib/server/load-user-account.ts b/app/home/(user)/_lib/server/load-user-account.ts index a16108a..c40e254 100644 --- a/app/home/(user)/_lib/server/load-user-account.ts +++ b/app/home/(user)/_lib/server/load-user-account.ts @@ -1,8 +1,9 @@ import { cache } from 'react'; +import { requireUserInServerComponent } from '@/lib/server/require-user-in-server-component'; + import { createAccountsApi } from '@kit/accounts/api'; import { getSupabaseServerClient } from '@kit/supabase/server-client'; -import { requireUserInServerComponent } from '@/lib/server/require-user-in-server-component'; export type UserAccount = Awaited>; diff --git a/app/home/(user)/_lib/server/load-user-analysis.ts b/app/home/(user)/_lib/server/load-user-analysis.ts index 5e70c3f..022bee1 100644 --- a/app/home/(user)/_lib/server/load-user-analysis.ts +++ b/app/home/(user)/_lib/server/load-user-analysis.ts @@ -1,8 +1,9 @@ import { cache } from 'react'; -import type { AnalysisResultDetailsMapped } from '@/packages/features/user-analyses/src/types/analysis-results'; -import { getSupabaseServerClient } from '@kit/supabase/server-client'; import { createUserAnalysesApi } from '@/packages/features/user-analyses/src/server/api'; +import type { AnalysisResultDetailsMapped } from '@/packages/features/user-analyses/src/types/analysis-results'; + +import { getSupabaseServerClient } from '@kit/supabase/server-client'; export type UserAnalyses = Awaited>; diff --git a/app/home/(user)/_lib/server/update-cart-partner-location.ts b/app/home/(user)/_lib/server/update-cart-partner-location.ts index 0ad1c6f..3a02b14 100644 --- a/app/home/(user)/_lib/server/update-cart-partner-location.ts +++ b/app/home/(user)/_lib/server/update-cart-partner-location.ts @@ -1,6 +1,6 @@ -"use server"; +'use server'; -import { retrieveCart, updateCart, updateLineItem } from "@lib/data/cart"; +import { retrieveCart, updateCart, updateLineItem } from '@lib/data/cart'; export const updateCartPartnerLocation = async ({ cartId, @@ -15,7 +15,7 @@ export const updateCartPartnerLocation = async ({ }) => { const cart = await retrieveCart(cartId); if (!cart) { - throw new Error("Cart not found"); + throw new Error('Cart not found'); } for (const lineItemId of lineIds) { @@ -35,4 +35,4 @@ export const updateCartPartnerLocation = async ({ partner_location_id: partnerLocationId, }, }); -} +}; diff --git a/app/home/(user)/billing/_components/personal-account-checkout-form.tsx b/app/home/(user)/billing/_components/personal-account-checkout-form.tsx index aacc8e5..bcece00 100644 --- a/app/home/(user)/billing/_components/personal-account-checkout-form.tsx +++ b/app/home/(user)/billing/_components/personal-account-checkout-form.tsx @@ -7,6 +7,7 @@ import dynamic from 'next/dynamic'; import { ExclamationTriangleIcon } from '@radix-ui/react-icons'; import { PlanPicker } from '@kit/billing-gateway/components'; +import { billingConfig } from '@kit/shared/config'; import { useAppEvents } from '@kit/shared/events'; import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert'; import { @@ -19,8 +20,6 @@ import { import { If } from '@kit/ui/if'; import { Trans } from '@kit/ui/trans'; -import { billingConfig } from '@kit/shared/config'; - import { createPersonalAccountCheckoutSession } from '../_lib/server/server-actions'; const EmbeddedCheckout = dynamic( diff --git a/app/home/(user)/billing/_lib/server/server-actions.ts b/app/home/(user)/billing/_lib/server/server-actions.ts index 8bec904..cbcbb70 100644 --- a/app/home/(user)/billing/_lib/server/server-actions.ts +++ b/app/home/(user)/billing/_lib/server/server-actions.ts @@ -3,11 +3,11 @@ import { redirect } from 'next/navigation'; import { enhanceAction } from '@kit/next/actions'; +import { featureFlagsConfig } from '@kit/shared/config'; import { getSupabaseServerClient } from '@kit/supabase/server-client'; import { PersonalAccountCheckoutSchema } from '../schema/personal-account-checkout.schema'; import { createUserBillingService } from './user-billing.service'; -import { featureFlagsConfig } from '@kit/shared/config'; /** * @name enabled diff --git a/app/home/(user)/billing/_lib/server/user-billing.service.ts b/app/home/(user)/billing/_lib/server/user-billing.service.ts index dd1f00b..afe34dc 100644 --- a/app/home/(user)/billing/_lib/server/user-billing.service.ts +++ b/app/home/(user)/billing/_lib/server/user-billing.service.ts @@ -8,12 +8,10 @@ import { z } from 'zod'; import { createAccountsApi } from '@kit/accounts/api'; import { getProductPlanPair } from '@kit/billing'; import { getBillingGatewayProvider } from '@kit/billing-gateway'; -import { getLogger } from '@kit/shared/logger'; -import { requireUser } from '@kit/supabase/require-user'; - import { appConfig, billingConfig } from '@kit/shared/config'; import { pathsConfig } from '@kit/shared/config'; - +import { getLogger } from '@kit/shared/logger'; +import { requireUser } from '@kit/supabase/require-user'; import { PersonalAccountCheckoutSchema } from '../schema/personal-account-checkout.schema'; diff --git a/app/home/(user)/billing/layout.tsx b/app/home/(user)/billing/layout.tsx index 7b3c5cb..540ba43 100644 --- a/app/home/(user)/billing/layout.tsx +++ b/app/home/(user)/billing/layout.tsx @@ -1,6 +1,6 @@ -import { featureFlagsConfig } from '@kit/shared/config'; import { notFound } from 'next/navigation'; +import { featureFlagsConfig } from '@kit/shared/config'; function UserBillingLayout(props: React.PropsWithChildren) { const isEnabled = featureFlagsConfig.enablePersonalAccountBilling; diff --git a/app/home/(user)/billing/page.tsx b/app/home/(user)/billing/page.tsx index 62484b4..461e053 100644 --- a/app/home/(user)/billing/page.tsx +++ b/app/home/(user)/billing/page.tsx @@ -3,17 +3,17 @@ import { CurrentLifetimeOrderCard, CurrentSubscriptionCard, } from '@kit/billing-gateway/components'; +import { billingConfig } from '@kit/shared/config'; import { AppBreadcrumbs } from '@kit/ui/app-breadcrumbs'; import { If } from '@kit/ui/if'; import { PageBody } from '@kit/ui/page'; import { Trans } from '@kit/ui/trans'; -import { billingConfig } from '@kit/shared/config'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; -import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component'; - // local imports import { withI18n } from '~/lib/i18n/with-i18n'; +import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component'; + import { HomeLayoutPageHeader } from '../_components/home-page-header'; import { createPersonalAccountBillingPortalSession } from '../billing/_lib/server/server-actions'; import { PersonalAccountCheckoutForm } from './_components/personal-account-checkout-form'; diff --git a/app/home/(user)/settings/_components/account-preferences-form.tsx b/app/home/(user)/settings/_components/account-preferences-form.tsx index f36adb3..54aa472 100644 --- a/app/home/(user)/settings/_components/account-preferences-form.tsx +++ b/app/home/(user)/settings/_components/account-preferences-form.tsx @@ -4,11 +4,12 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { Trans } from 'react-i18next'; -import type { AccountWithParams } from '@kit/accounts/types/accounts'; import { useRevalidatePersonalAccountDataQuery } from '@kit/accounts/hooks/use-personal-account-data'; +import type { AccountWithParams } from '@kit/accounts/types/accounts'; import { Button } from '@kit/ui/button'; import { Card, CardDescription, CardTitle } from '@kit/ui/card'; import { Form } from '@kit/ui/form'; +import { LanguageSelector } from '@kit/ui/language-selector'; import { toast } from '@kit/ui/sonner'; import { Switch } from '@kit/ui/switch'; @@ -17,7 +18,6 @@ import { accountPreferencesSchema, } from '../_lib/account-preferences.schema'; import { updatePersonalAccountPreferencesAction } from '../_lib/server/actions'; -import { LanguageSelector } from '@kit/ui/language-selector'; export default function AccountPreferencesForm({ account, diff --git a/app/home/(user)/settings/_components/account-settings-form.tsx b/app/home/(user)/settings/_components/account-settings-form.tsx index 5aba272..2aaa91e 100644 --- a/app/home/(user)/settings/_components/account-settings-form.tsx +++ b/app/home/(user)/settings/_components/account-settings-form.tsx @@ -4,8 +4,8 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { Trans } from 'react-i18next'; -import type { AccountWithParams } from '@kit/accounts/types/accounts'; import { useRevalidatePersonalAccountDataQuery } from '@kit/accounts/hooks/use-personal-account-data'; +import type { AccountWithParams } from '@kit/accounts/types/accounts'; import { Button } from '@kit/ui/button'; import { Form, @@ -129,11 +129,7 @@ export default function AccountSettingsForm({ - + @@ -152,11 +148,7 @@ export default function AccountSettingsForm({ - + diff --git a/app/home/(user)/settings/_components/settings-navigation.tsx b/app/home/(user)/settings/_components/settings-navigation.tsx index 4812349..157a477 100644 --- a/app/home/(user)/settings/_components/settings-navigation.tsx +++ b/app/home/(user)/settings/_components/settings-navigation.tsx @@ -9,6 +9,8 @@ import { Cross, Menu, Shield, ShoppingCart } 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 } from '@kit/shared/config'; import { useSignOut } from '@kit/supabase/hooks/use-sign-out'; import { @@ -22,8 +24,6 @@ import { import { If } from '@kit/ui/if'; import { Trans } from '@kit/ui/trans'; -import SignOutDropdownItem from '@kit/shared/components/sign-out-dropdown-item'; -import DropdownLink from '@kit/shared/components/ui/dropdown-link'; import { UserWorkspace } from '../../_lib/server/load-user-workspace'; import { routes } from './settings-sidebar'; @@ -148,5 +148,3 @@ export function SettingsMobileNavigation(props: { ); } - - diff --git a/app/home/(user)/settings/_components/settings-section-header.tsx b/app/home/(user)/settings/_components/settings-section-header.tsx index 36eaa0a..3381e2e 100644 --- a/app/home/(user)/settings/_components/settings-section-header.tsx +++ b/app/home/(user)/settings/_components/settings-section-header.tsx @@ -1,5 +1,5 @@ -import { Separator } from "@kit/ui/separator"; -import { Trans } from "@kit/ui/trans"; +import { Separator } from '@kit/ui/separator'; +import { Trans } from '@kit/ui/trans'; export default function SettingsSectionHeader({ titleKey, diff --git a/app/home/(user)/settings/layout.tsx b/app/home/(user)/settings/layout.tsx index d93b963..ffdde34 100644 --- a/app/home/(user)/settings/layout.tsx +++ b/app/home/(user)/settings/layout.tsx @@ -11,11 +11,11 @@ import { SidebarProvider } from '@kit/ui/shadcn-sidebar'; import { withI18n } from '~/lib/i18n/with-i18n'; -import { SettingsSidebar } from './_components/settings-sidebar'; // home imports import { HomeMenuNavigation } from '../_components/home-menu-navigation'; import { loadUserWorkspace } from '../_lib/server/load-user-workspace'; import { SettingsMobileNavigation } from './_components/settings-navigation'; +import { SettingsSidebar } from './_components/settings-sidebar'; function UserSettingsLayout({ children }: React.PropsWithChildren) { return {children}; @@ -27,7 +27,6 @@ function HeaderLayout({ children }: React.PropsWithChildren) { const workspace = use(loadUserWorkspace()); const cart = use(retrieveCart()); - return ( @@ -44,7 +43,7 @@ function HeaderLayout({ children }: React.PropsWithChildren) { -
{children}
+
{children}
diff --git a/app/home/[account]/_components/team-account-accounts-selector.tsx b/app/home/[account]/_components/team-account-accounts-selector.tsx index 4b6fc7b..12cbcc0 100644 --- a/app/home/[account]/_components/team-account-accounts-selector.tsx +++ b/app/home/[account]/_components/team-account-accounts-selector.tsx @@ -5,11 +5,9 @@ import { useContext } from 'react'; import { useRouter } from 'next/navigation'; import { AccountSelector } from '@kit/accounts/account-selector'; +import { featureFlagsConfig, pathsConfig } from '@kit/shared/config'; import { SidebarContext } from '@kit/ui/shadcn-sidebar'; -import { pathsConfig, featureFlagsConfig } from '@kit/shared/config'; - - const features = { enableTeamCreation: featureFlagsConfig.enableTeamCreation, }; diff --git a/app/home/[account]/_components/team-account-layout-mobile-navigation.tsx b/app/home/[account]/_components/team-account-layout-mobile-navigation.tsx index 70fb207..356e309 100644 --- a/app/home/[account]/_components/team-account-layout-mobile-navigation.tsx +++ b/app/home/[account]/_components/team-account-layout-mobile-navigation.tsx @@ -1,12 +1,12 @@ 'use client'; -import DropdownLink from '@kit/shared/components/ui/dropdown-link'; import { useRouter } from 'next/navigation'; -import SignOutDropdownItem from '@kit/shared/components/sign-out-dropdown-item'; import { Home, Menu } from 'lucide-react'; import { AccountSelector } from '@kit/accounts/account-selector'; +import SignOutDropdownItem from '@kit/shared/components/sign-out-dropdown-item'; +import DropdownLink from '@kit/shared/components/ui/dropdown-link'; import { featureFlagsConfig, getTeamAccountSidebarConfig, @@ -93,8 +93,6 @@ export const TeamAccountLayoutMobileNavigation = ( ); }; - - function TeamAccountsModal(props: { accounts: Accounts; userId: string; diff --git a/app/home/[account]/_components/team-account-layout-sidebar-navigation.tsx b/app/home/[account]/_components/team-account-layout-sidebar-navigation.tsx deleted file mode 100644 index 9e1eeb4..0000000 --- a/app/home/[account]/_components/team-account-layout-sidebar-navigation.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { z } from 'zod'; - -import { NavigationConfigSchema } from '@kit/ui/navigation-schema'; -import { SidebarNavigation } from '@kit/ui/shadcn-sidebar'; - -export function TeamAccountLayoutSidebarNavigation({ - config, -}: React.PropsWithChildren<{ - config: z.infer; -}>) { - return ; -} diff --git a/app/home/[account]/_components/team-account-layout-sidebar.tsx b/app/home/[account]/_components/team-account-layout-sidebar.tsx index 76dd94f..f2c9626 100644 --- a/app/home/[account]/_components/team-account-layout-sidebar.tsx +++ b/app/home/[account]/_components/team-account-layout-sidebar.tsx @@ -1,20 +1,12 @@ -import type { User } from '@supabase/supabase-js'; - import { ApplicationRole } from '@kit/accounts/types/accounts'; -import { ProfileAccountDropdownContainer } from '@kit/shared/components/personal-account-dropdown-container'; import { getTeamAccountSidebarConfig } from '@kit/shared/config'; import { Sidebar, SidebarContent, - SidebarFooter, SidebarHeader, + SidebarNavigation, } from '@kit/ui/shadcn-sidebar'; -import { TeamAccountNotifications } from '~/home/[account]/_components/team-account-notifications'; - -import { TeamAccountAccountsSelector } from '../_components/team-account-accounts-selector'; -import { TeamAccountLayoutSidebarNavigation } from './team-account-layout-sidebar-navigation'; - type AccountModel = { label: string | null; value: string | null; @@ -26,14 +18,12 @@ export function TeamAccountLayoutSidebar(props: { account: string; accountId: string; accounts: AccountModel[]; - user: User; }) { return ( ); } @@ -42,45 +32,27 @@ function SidebarContainer(props: { account: string; accountId: string; accounts: AccountModel[]; - user: User; }) { - const { account, accounts, user } = props; - const userId = user.id; + const { account, accounts } = props; const config = getTeamAccountSidebarConfig(account); const collapsible = config.sidebarCollapsedStyle; + const selectedAccount = accounts.find(({ value }) => value === account); + const accountName = selectedAccount?.label || account; + return ( - -
- - -
- -
+ +
+
{accountName}
- - + + - - - - - ); } diff --git a/app/home/[account]/_components/team-account-notifications.tsx b/app/home/[account]/_components/team-account-notifications.tsx index 5344490..eec9503 100644 --- a/app/home/[account]/_components/team-account-notifications.tsx +++ b/app/home/[account]/_components/team-account-notifications.tsx @@ -1,8 +1,6 @@ import { NotificationsPopover } from '@kit/notifications/components'; - import { featureFlagsConfig } from '@kit/shared/config'; - export function TeamAccountNotifications(params: { userId: string; accountId: string; diff --git a/app/home/[account]/_lib/server/team-account-workspace.loader.ts b/app/home/[account]/_lib/server/team-account-workspace.loader.ts index 3ecaaee..49a5c9f 100644 --- a/app/home/[account]/_lib/server/team-account-workspace.loader.ts +++ b/app/home/[account]/_lib/server/team-account-workspace.loader.ts @@ -4,11 +4,10 @@ import { cache } from 'react'; import { redirect } from 'next/navigation'; +import { pathsConfig } from '@kit/shared/config'; import { getSupabaseServerClient } from '@kit/supabase/server-client'; import { createTeamAccountsApi } from '@kit/team-accounts/api'; -import { pathsConfig } from '@kit/shared/config'; - import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component'; export type TeamAccountWorkspace = Awaited< @@ -29,7 +28,7 @@ export const loadTeamWorkspace = cache(workspaceLoader); async function workspaceLoader(accountSlug: string) { const client = getSupabaseServerClient(); const api = createTeamAccountsApi(client); - const user = await requireUserInServerComponent(); + const user = await requireUserInServerComponent(); const workspace = await api.getAccountWorkspace(accountSlug, user.id); // we cannot find any record for the selected account // so we redirect the user to the home page @@ -39,7 +38,9 @@ async function workspaceLoader(accountSlug: string) { return { ...workspace.data, - accounts: workspace.data.accounts.map(({ user_accounts }) => ({...user_accounts})), + accounts: workspace.data.accounts.map(({ user_accounts }) => ({ + ...user_accounts, + })), user, }; } diff --git a/app/home/[account]/billing/_components/health-benefit-fields.tsx b/app/home/[account]/billing/_components/health-benefit-fields.tsx index 7428f82..55c533c 100644 --- a/app/home/[account]/billing/_components/health-benefit-fields.tsx +++ b/app/home/[account]/billing/_components/health-benefit-fields.tsx @@ -20,7 +20,7 @@ const HealthBenefitFields = () => { return (
( @@ -30,20 +30,20 @@ const HealthBenefitFields = () => { { />
- + Sign in - - Not a member?{" "} + + Not a member?{' '}
- ) -} + ); +}; -export default Login +export default Login; diff --git a/packages/features/medusa-storefront/src/modules/account/components/order-card/index.tsx b/packages/features/medusa-storefront/src/modules/account/components/order-card/index.tsx index d877231..a6ea23d 100644 --- a/packages/features/medusa-storefront/src/modules/account/components/order-card/index.tsx +++ b/packages/features/medusa-storefront/src/modules/account/components/order-card/index.tsx @@ -1,34 +1,34 @@ -import { Button } from "@medusajs/ui" -import { useMemo } from "react" +import { useMemo } from 'react'; -import Thumbnail from "@modules/products/components/thumbnail" -import LocalizedClientLink from "@modules/common/components/localized-client-link" -import { convertToLocale } from "@lib/util/money" -import { HttpTypes } from "@medusajs/types" +import { convertToLocale } from '@lib/util/money'; +import { HttpTypes } from '@medusajs/types'; +import { Button } from '@medusajs/ui'; +import LocalizedClientLink from '@modules/common/components/localized-client-link'; +import Thumbnail from '@modules/products/components/thumbnail'; type OrderCardProps = { - order: HttpTypes.StoreOrder -} + order: HttpTypes.StoreOrder; +}; const OrderCard = ({ order }: OrderCardProps) => { const numberOfLines = useMemo(() => { return ( order.items?.reduce((acc, item) => { - return acc + item.quantity + return acc + item.quantity; }, 0) ?? 0 - ) - }, [order]) + ); + }, [order]); const numberOfProducts = useMemo(() => { - return order.items?.length ?? 0 - }, [order]) + return order.items?.length ?? 0; + }, [order]); return ( -
-
+
+
#{order.display_id}
-
+
{new Date(order.created_at).toDateString()} @@ -39,10 +39,10 @@ const OrderCard = ({ order }: OrderCardProps) => { })} {`${numberOfLines} ${ - numberOfLines > 1 ? "items" : "item" + numberOfLines > 1 ? 'items' : 'item' }`}
-
+
{order.items?.slice(0, 3).map((i) => { return (
{ data-testid="order-item" > -
+
{ {i.quantity}
- ) + ); })} {numberOfProducts > 4 && ( -
+
+ {numberOfLines - 4} @@ -81,7 +81,7 @@ const OrderCard = ({ order }: OrderCardProps) => {
- ) -} + ); +}; -export default OrderCard +export default OrderCard; diff --git a/packages/features/medusa-storefront/src/modules/account/components/order-overview/index.tsx b/packages/features/medusa-storefront/src/modules/account/components/order-overview/index.tsx index 35ffe0b..7d8e4a6 100644 --- a/packages/features/medusa-storefront/src/modules/account/components/order-overview/index.tsx +++ b/packages/features/medusa-storefront/src/modules/account/components/order-overview/index.tsx @@ -1,35 +1,35 @@ -"use client" +'use client'; -import { Button } from "@medusajs/ui" +import { HttpTypes } from '@medusajs/types'; +import { Button } from '@medusajs/ui'; +import LocalizedClientLink from '@modules/common/components/localized-client-link'; -import OrderCard from "../order-card" -import LocalizedClientLink from "@modules/common/components/localized-client-link" -import { HttpTypes } from "@medusajs/types" +import OrderCard from '../order-card'; const OrderOverview = ({ orders }: { orders: HttpTypes.StoreOrder[] }) => { if (orders?.length) { return ( -
+
{orders.map((o) => (
))}
- ) + ); } return (

Nothing to see here

- You don't have any orders yet, let us change that {":)"} + You don't have any orders yet, let us change that {':)'}

@@ -39,7 +39,7 @@ const OrderOverview = ({ orders }: { orders: HttpTypes.StoreOrder[] }) => {
- ) -} + ); +}; -export default OrderOverview +export default OrderOverview; diff --git a/packages/features/medusa-storefront/src/modules/account/components/overview/index.tsx b/packages/features/medusa-storefront/src/modules/account/components/overview/index.tsx index d807e97..7476170 100644 --- a/packages/features/medusa-storefront/src/modules/account/components/overview/index.tsx +++ b/packages/features/medusa-storefront/src/modules/account/components/overview/index.tsx @@ -1,25 +1,24 @@ -import { Container } from "@medusajs/ui" - -import ChevronDown from "@modules/common/icons/chevron-down" -import LocalizedClientLink from "@modules/common/components/localized-client-link" -import { convertToLocale } from "@lib/util/money" -import { HttpTypes } from "@medusajs/types" +import { convertToLocale } from '@lib/util/money'; +import { HttpTypes } from '@medusajs/types'; +import { Container } from '@medusajs/ui'; +import LocalizedClientLink from '@modules/common/components/localized-client-link'; +import ChevronDown from '@modules/common/icons/chevron-down'; type OverviewProps = { - customer: HttpTypes.StoreCustomer | null - orders: HttpTypes.StoreOrder[] | null -} + customer: HttpTypes.StoreCustomer | null; + orders: HttpTypes.StoreOrder[] | null; +}; const Overview = ({ customer, orders }: OverviewProps) => { return (
-
-
+
+
Hello {customer?.first_name} - Signed in as:{" "} + Signed in as:{' '} {
-
-
-
+
+
+

Profile

@@ -42,7 +41,7 @@ const Overview = ({ customer, orders }: OverviewProps) => { > {getProfileCompletion(customer)}% - + Completed
@@ -58,7 +57,7 @@ const Overview = ({ customer, orders }: OverviewProps) => { > {customer?.addresses?.length || 0} - + Saved
@@ -84,8 +83,8 @@ const Overview = ({ customer, orders }: OverviewProps) => { - -
+ +
Date placed Order number @@ -121,7 +120,7 @@ const Overview = ({ customer, orders }: OverviewProps) => { - ) + ); }) ) : ( No recent orders @@ -132,37 +131,37 @@ const Overview = ({ customer, orders }: OverviewProps) => {
- ) -} + ); +}; const getProfileCompletion = (customer: HttpTypes.StoreCustomer | null) => { - let count = 0 + let count = 0; if (!customer) { - return 0 + return 0; } if (customer.email) { - count++ + count++; } if (customer.first_name && customer.last_name) { - count++ + count++; } if (customer.phone) { - count++ + count++; } const billingAddress = customer.addresses?.find( - (addr) => addr.is_default_billing - ) + (addr) => addr.is_default_billing, + ); if (billingAddress) { - count++ + count++; } - return (count / 4) * 100 -} + return (count / 4) * 100; +}; -export default Overview +export default Overview; diff --git a/packages/features/medusa-storefront/src/modules/account/components/profile-billing-address/index.tsx b/packages/features/medusa-storefront/src/modules/account/components/profile-billing-address/index.tsx index 95ce947..5ed5621 100644 --- a/packages/features/medusa-storefront/src/modules/account/components/profile-billing-address/index.tsx +++ b/packages/features/medusa-storefront/src/modules/account/components/profile-billing-address/index.tsx @@ -1,18 +1,18 @@ -"use client" +'use client'; -import React, { useEffect, useMemo, useActionState } from "react" +import React, { useActionState, useEffect, useMemo } from 'react'; -import Input from "@modules/common/components/input" -import NativeSelect from "@modules/common/components/native-select" +import { addCustomerAddress, updateCustomerAddress } from '@lib/data/customer'; +import { HttpTypes } from '@medusajs/types'; +import Input from '@modules/common/components/input'; +import NativeSelect from '@modules/common/components/native-select'; -import AccountInfo from "../account-info" -import { HttpTypes } from "@medusajs/types" -import { addCustomerAddress, updateCustomerAddress } from "@lib/data/customer" +import AccountInfo from '../account-info'; type MyInformationProps = { - customer: HttpTypes.StoreCustomer - regions: HttpTypes.StoreRegion[] -} + customer: HttpTypes.StoreCustomer; + regions: HttpTypes.StoreRegion[]; +}; const ProfileBillingAddress: React.FC = ({ customer, @@ -25,51 +25,51 @@ const ProfileBillingAddress: React.FC = ({ return region.countries?.map((country) => ({ value: country.iso_2, label: country.display_name, - })) + })); }) .flat() || [] - ) - }, [regions]) + ); + }, [regions]); - const [successState, setSuccessState] = React.useState(false) + const [successState, setSuccessState] = React.useState(false); const billingAddress = customer.addresses?.find( - (addr) => addr.is_default_billing - ) + (addr) => addr.is_default_billing, + ); const initialState: Record = { isDefaultBilling: true, isDefaultShipping: false, error: false, success: false, - } + }; if (billingAddress) { - initialState.addressId = billingAddress.id + initialState.addressId = billingAddress.id; } const [state, formAction] = useActionState( billingAddress ? updateCustomerAddress : addCustomerAddress, - initialState - ) + initialState, + ); const clearState = () => { - setSuccessState(false) - } + setSuccessState(false); + }; useEffect(() => { - setSuccessState(state.success) - }, [state]) + setSuccessState(state.success); + }, [state]); const currentInfo = useMemo(() => { if (!billingAddress) { - return "No billing address" + return 'No billing address'; } const country = regionOptions?.find( - (country) => country?.value === billingAddress.country_code - )?.label || billingAddress.country_code?.toUpperCase() + (country) => country?.value === billingAddress.country_code, + )?.label || billingAddress.country_code?.toUpperCase(); return (
@@ -79,15 +79,15 @@ const ProfileBillingAddress: React.FC = ({ {billingAddress.company} {billingAddress.address_1} - {billingAddress.address_2 ? `, ${billingAddress.address_2}` : ""} + {billingAddress.address_2 ? `, ${billingAddress.address_2}` : ''} {billingAddress.postal_code}, {billingAddress.city} {country}
- ) - }, [billingAddress, regionOptions]) + ); + }, [billingAddress, regionOptions]); return (
clearState()} className="w-full"> @@ -170,13 +170,13 @@ const ProfileBillingAddress: React.FC = ({ - ) + ); })}
- ) -} + ); +}; -export default ProfileBillingAddress +export default ProfileBillingAddress; diff --git a/packages/features/medusa-storefront/src/modules/account/components/profile-email/index.tsx b/packages/features/medusa-storefront/src/modules/account/components/profile-email/index.tsx index 48b0bf0..99fd92e 100644 --- a/packages/features/medusa-storefront/src/modules/account/components/profile-email/index.tsx +++ b/packages/features/medusa-storefront/src/modules/account/components/profile-email/index.tsx @@ -1,49 +1,50 @@ -"use client" +'use client'; -import React, { useEffect, useActionState } from "react"; +import React, { useActionState, useEffect } from 'react'; -import Input from "@modules/common/components/input" +import { HttpTypes } from '@medusajs/types'; +import Input from '@modules/common/components/input'; + +import AccountInfo from '../account-info'; -import AccountInfo from "../account-info" -import { HttpTypes } from "@medusajs/types" // import { updateCustomer } from "@lib/data/customer" type MyInformationProps = { - customer: HttpTypes.StoreCustomer -} + customer: HttpTypes.StoreCustomer; +}; const ProfileEmail: React.FC = ({ customer }) => { - const [successState, setSuccessState] = React.useState(false) + const [successState, setSuccessState] = React.useState(false); // TODO: It seems we don't support updating emails now? const updateCustomerEmail = ( _currentState: Record, - formData: FormData + formData: FormData, ) => { const customer = { - email: formData.get("email") as string, - } + email: formData.get('email') as string, + }; try { // await updateCustomer(customer) - return { success: true, error: null } + return { success: true, error: null }; } catch (error: any) { - return { success: false, error: error.toString() } + return { success: false, error: error.toString() }; } - } + }; const [state, formAction] = useActionState(updateCustomerEmail, { error: false, success: false, - }) + }); const clearState = () => { - setSuccessState(false) - } + setSuccessState(false); + }; useEffect(() => { - setSuccessState(state.success) - }, [state]) + setSuccessState(state.success); + }, [state]); return (
@@ -69,7 +70,7 @@ const ProfileEmail: React.FC = ({ customer }) => {
- ) -} + ); +}; -export default ProfileEmail +export default ProfileEmail; diff --git a/packages/features/medusa-storefront/src/modules/account/components/profile-name/index.tsx b/packages/features/medusa-storefront/src/modules/account/components/profile-name/index.tsx index be9c258..a30bdce 100644 --- a/packages/features/medusa-storefront/src/modules/account/components/profile-name/index.tsx +++ b/packages/features/medusa-storefront/src/modules/account/components/profile-name/index.tsx @@ -1,49 +1,49 @@ -"use client" +'use client'; -import React, { useEffect, useActionState } from "react"; +import React, { useActionState, useEffect } from 'react'; -import Input from "@modules/common/components/input" +import { updateCustomer } from '@lib/data/customer'; +import { HttpTypes } from '@medusajs/types'; +import Input from '@modules/common/components/input'; -import AccountInfo from "../account-info" -import { HttpTypes } from "@medusajs/types" -import { updateCustomer } from "@lib/data/customer" +import AccountInfo from '../account-info'; type MyInformationProps = { - customer: HttpTypes.StoreCustomer -} + customer: HttpTypes.StoreCustomer; +}; const ProfileName: React.FC = ({ customer }) => { - const [successState, setSuccessState] = React.useState(false) + const [successState, setSuccessState] = React.useState(false); const updateCustomerName = async ( _currentState: Record, - formData: FormData + formData: FormData, ) => { const customer = { - first_name: formData.get("first_name") as string, - last_name: formData.get("last_name") as string, - } + first_name: formData.get('first_name') as string, + last_name: formData.get('last_name') as string, + }; try { - await updateCustomer(customer) - return { success: true, error: null } + await updateCustomer(customer); + return { success: true, error: null }; } catch (error: any) { - return { success: false, error: error.toString() } + return { success: false, error: error.toString() }; } - } + }; const [state, formAction] = useActionState(updateCustomerName, { error: false, success: false, - }) + }); const clearState = () => { - setSuccessState(false) - } + setSuccessState(false); + }; useEffect(() => { - setSuccessState(state.success) - }, [state]) + setSuccessState(state.success); + }, [state]); return (
@@ -60,20 +60,20 @@ const ProfileName: React.FC = ({ customer }) => { label="First name" name="first_name" required - defaultValue={customer.first_name ?? ""} + defaultValue={customer.first_name ?? ''} data-testid="first-name-input" />
- ) -} + ); +}; -export default ProfileName +export default ProfileName; diff --git a/packages/features/medusa-storefront/src/modules/account/components/profile-password/index.tsx b/packages/features/medusa-storefront/src/modules/account/components/profile-password/index.tsx index 63e8d90..a6abaa1 100644 --- a/packages/features/medusa-storefront/src/modules/account/components/profile-password/index.tsx +++ b/packages/features/medusa-storefront/src/modules/account/components/profile-password/index.tsx @@ -1,26 +1,28 @@ -"use client" +'use client'; -import React, { useEffect, useActionState } from "react" -import Input from "@modules/common/components/input" -import AccountInfo from "../account-info" -import { HttpTypes } from "@medusajs/types" -import { toast } from "@medusajs/ui" +import React, { useActionState, useEffect } from 'react'; + +import { HttpTypes } from '@medusajs/types'; +import { toast } from '@medusajs/ui'; +import Input from '@modules/common/components/input'; + +import AccountInfo from '../account-info'; type MyInformationProps = { - customer: HttpTypes.StoreCustomer -} + customer: HttpTypes.StoreCustomer; +}; const ProfilePassword: React.FC = ({ customer }) => { - const [successState, setSuccessState] = React.useState(false) + const [successState, setSuccessState] = React.useState(false); // TODO: Add support for password updates const updatePassword = async () => { - toast.info("Password update is not implemented") - } + toast.info('Password update is not implemented'); + }; const clearState = () => { - setSuccessState(false) - } + setSuccessState(false); + }; return (
= ({ customer }) => {
- ) -} + ); +}; -export default ProfilePassword +export default ProfilePassword; diff --git a/packages/features/medusa-storefront/src/modules/account/components/profile-phone/index.tsx b/packages/features/medusa-storefront/src/modules/account/components/profile-phone/index.tsx index d1a8b6b..f99cf62 100644 --- a/packages/features/medusa-storefront/src/modules/account/components/profile-phone/index.tsx +++ b/packages/features/medusa-storefront/src/modules/account/components/profile-phone/index.tsx @@ -1,48 +1,48 @@ -"use client" +'use client'; -import React, { useEffect, useActionState } from "react"; +import React, { useActionState, useEffect } from 'react'; -import Input from "@modules/common/components/input" +import { updateCustomer } from '@lib/data/customer'; +import { HttpTypes } from '@medusajs/types'; +import Input from '@modules/common/components/input'; -import AccountInfo from "../account-info" -import { HttpTypes } from "@medusajs/types" -import { updateCustomer } from "@lib/data/customer" +import AccountInfo from '../account-info'; type MyInformationProps = { - customer: HttpTypes.StoreCustomer -} + customer: HttpTypes.StoreCustomer; +}; const ProfileEmail: React.FC = ({ customer }) => { - const [successState, setSuccessState] = React.useState(false) + const [successState, setSuccessState] = React.useState(false); const updateCustomerPhone = async ( _currentState: Record, - formData: FormData + formData: FormData, ) => { const customer = { - phone: formData.get("phone") as string, - } + phone: formData.get('phone') as string, + }; try { - await updateCustomer(customer) - return { success: true, error: null } + await updateCustomer(customer); + return { success: true, error: null }; } catch (error: any) { - return { success: false, error: error.toString() } + return { success: false, error: error.toString() }; } - } + }; const [state, formAction] = useActionState(updateCustomerPhone, { error: false, success: false, - }) + }); const clearState = () => { - setSuccessState(false) - } + setSuccessState(false); + }; useEffect(() => { - setSuccessState(state.success) - }, [state]) + setSuccessState(state.success); + }, [state]); return (
@@ -62,13 +62,13 @@ const ProfileEmail: React.FC = ({ customer }) => { type="phone" autoComplete="phone" required - defaultValue={customer.phone ?? ""} + defaultValue={customer.phone ?? ''} data-testid="phone-input" />
- ) -} + ); +}; -export default ProfileEmail +export default ProfileEmail; diff --git a/packages/features/medusa-storefront/src/modules/account/components/register/index.tsx b/packages/features/medusa-storefront/src/modules/account/components/register/index.tsx index 197d7fc..584ee2c 100644 --- a/packages/features/medusa-storefront/src/modules/account/components/register/index.tsx +++ b/packages/features/medusa-storefront/src/modules/account/components/register/index.tsx @@ -1,34 +1,35 @@ -"use client" +'use client'; -import { useActionState } from "react" -import Input from "@modules/common/components/input" -import { LOGIN_VIEW } from "@modules/account/templates/login-template" -import ErrorMessage from "@modules/checkout/components/error-message" -import { SubmitButton } from "@modules/checkout/components/submit-button" -import LocalizedClientLink from "@modules/common/components/localized-client-link" -import { signup } from "@lib/data/customer" +import { useActionState } from 'react'; + +import { signup } from '@lib/data/customer'; +import { LOGIN_VIEW } from '@modules/account/templates/login-template'; +import ErrorMessage from '@modules/checkout/components/error-message'; +import { SubmitButton } from '@modules/checkout/components/submit-button'; +import Input from '@modules/common/components/input'; +import LocalizedClientLink from '@modules/common/components/localized-client-link'; type Props = { - setCurrentView: (view: LOGIN_VIEW) => void -} + setCurrentView: (view: LOGIN_VIEW) => void; +}; const Register = ({ setCurrentView }: Props) => { - const [message, formAction] = useActionState(signup, null) + const [message, formAction] = useActionState(signup, null); return (
-

+

Become a Medusa Store Member

-

+

Create your Medusa Store Member profile, and get access to an enhanced shopping experience.

-
-
+ +
{ />
- - By creating an account, you agree to Medusa Store's{" "} + + By creating an account, you agree to Medusa Store's{' '} Privacy Policy - {" "} - and{" "} + {' '} + and{' '} { . - + Join - - Already a member?{" "} + + Already a member?{' '}
- ) -} + ); +}; -export default Register +export default Register; diff --git a/packages/features/medusa-storefront/src/modules/account/components/transfer-request-form/index.tsx b/packages/features/medusa-storefront/src/modules/account/components/transfer-request-form/index.tsx index c859f32..50fe520 100644 --- a/packages/features/medusa-storefront/src/modules/account/components/transfer-request-form/index.tsx +++ b/packages/features/medusa-storefront/src/modules/account/components/transfer-request-form/index.tsx @@ -1,30 +1,38 @@ -"use client" +'use client'; -import { useActionState } from "react" -import { createTransferRequest } from "@lib/data/orders" -import { Text, Heading, Input, Button, IconButton, Toaster } from "@medusajs/ui" -import { SubmitButton } from "@modules/checkout/components/submit-button" -import { CheckCircleMiniSolid, XCircleSolid } from "@medusajs/icons" -import { useEffect, useState } from "react" +import { useActionState } from 'react'; +import { useEffect, useState } from 'react'; + +import { createTransferRequest } from '@lib/data/orders'; +import { CheckCircleMiniSolid, XCircleSolid } from '@medusajs/icons'; +import { + Button, + Heading, + IconButton, + Input, + Text, + Toaster, +} from '@medusajs/ui'; +import { SubmitButton } from '@modules/checkout/components/submit-button'; export default function TransferRequestForm() { - const [showSuccess, setShowSuccess] = useState(false) + const [showSuccess, setShowSuccess] = useState(false); const [state, formAction] = useActionState(createTransferRequest, { success: false, error: null, order: null, - }) + }); useEffect(() => { if (state.success && state.order) { - setShowSuccess(true) + setShowSuccess(true); } - }, [state.success, state.order]) + }, [state.success, state.order]); return ( -
-
+
+
Order transfers @@ -38,11 +46,11 @@ export default function TransferRequestForm() { action={formAction} className="flex flex-col gap-y-1 sm:items-end" > -
+
Request transfer @@ -50,14 +58,14 @@ export default function TransferRequestForm() {
{!state.success && state.error && ( - + {state.error} )} {showSuccess && ( -
-
- +
+
+
Transfer for order {state.order?.id} requested @@ -72,10 +80,10 @@ export default function TransferRequestForm() { className="h-fit" onClick={() => setShowSuccess(false)} > - +
)}
- ) + ); } diff --git a/packages/features/medusa-storefront/src/modules/account/templates/account-layout.tsx b/packages/features/medusa-storefront/src/modules/account/templates/account-layout.tsx index 2d29490..799635b 100644 --- a/packages/features/medusa-storefront/src/modules/account/templates/account-layout.tsx +++ b/packages/features/medusa-storefront/src/modules/account/templates/account-layout.tsx @@ -1,13 +1,13 @@ -import React from "react" +import React from 'react'; -import UnderlineLink from "@modules/common/components/interactive-link" +import { HttpTypes } from '@medusajs/types'; +import UnderlineLink from '@modules/common/components/interactive-link'; -import AccountNav from "../components/account-nav" -import { HttpTypes } from "@medusajs/types" +import AccountNav from '../components/account-nav'; interface AccountLayoutProps { - customer: HttpTypes.StoreCustomer | null - children: React.ReactNode + customer: HttpTypes.StoreCustomer | null; + children: React.ReactNode; } const AccountLayout: React.FC = ({ @@ -15,13 +15,13 @@ const AccountLayout: React.FC = ({ children, }) => { return ( -
-
-
+
+
+
{customer && }
{children}
-
+

Got questions?

@@ -37,7 +37,7 @@ const AccountLayout: React.FC = ({
- ) -} + ); +}; -export default AccountLayout +export default AccountLayout; diff --git a/packages/features/medusa-storefront/src/modules/account/templates/index.ts b/packages/features/medusa-storefront/src/modules/account/templates/index.ts index 3507a36..8b34a7a 100644 --- a/packages/features/medusa-storefront/src/modules/account/templates/index.ts +++ b/packages/features/medusa-storefront/src/modules/account/templates/index.ts @@ -1,7 +1,7 @@ // account-info -export { default as AccountLayout } from "./account-layout"; -export * from "./account-layout"; +export { default as AccountLayout } from './account-layout'; +export * from './account-layout'; // account-nav -export { default as LoginTemplate } from "./login-template"; -export * from "./login-template"; +export { default as LoginTemplate } from './login-template'; +export * from './login-template'; diff --git a/packages/features/medusa-storefront/src/modules/account/templates/login-template.tsx b/packages/features/medusa-storefront/src/modules/account/templates/login-template.tsx index bbca313..afe8308 100644 --- a/packages/features/medusa-storefront/src/modules/account/templates/login-template.tsx +++ b/packages/features/medusa-storefront/src/modules/account/templates/login-template.tsx @@ -1,19 +1,20 @@ -"use client"; +'use client'; -import { useState } from "react"; -import { Login, Register } from "../components"; +import { useState } from 'react'; + +import { Login, Register } from '../components'; export enum LOGIN_VIEW { - SIGN_IN = "sign-in", - REGISTER = "register", + SIGN_IN = 'sign-in', + REGISTER = 'register', } const LoginTemplate = () => { - const [currentView, setCurrentView] = useState("sign-in"); + const [currentView, setCurrentView] = useState('sign-in'); return ( -
- {currentView === "sign-in" ? ( +
+ {currentView === 'sign-in' ? ( ) : ( diff --git a/packages/features/medusa-storefront/src/modules/cart/components/cart-item-select/index.tsx b/packages/features/medusa-storefront/src/modules/cart/components/cart-item-select/index.tsx index 0073df0..a9733f8 100644 --- a/packages/features/medusa-storefront/src/modules/cart/components/cart-item-select/index.tsx +++ b/packages/features/medusa-storefront/src/modules/cart/components/cart-item-select/index.tsx @@ -1,6 +1,5 @@ -"use client" +'use client'; -import { IconBadge, clx } from "@medusajs/ui" import { SelectHTMLAttributes, forwardRef, @@ -8,33 +7,34 @@ import { useImperativeHandle, useRef, useState, -} from "react" +} from 'react'; -import ChevronDown from "@modules/common/icons/chevron-down" +import { IconBadge, clx } from '@medusajs/ui'; +import ChevronDown from '@modules/common/icons/chevron-down'; type NativeSelectProps = { - placeholder?: string - errors?: Record - touched?: Record -} & Omit, "size"> + placeholder?: string; + errors?: Record; + touched?: Record; +} & Omit, 'size'>; const CartItemSelect = forwardRef( - ({ placeholder = "Select...", className, children, ...props }, ref) => { - const innerRef = useRef(null) - const [isPlaceholder, setIsPlaceholder] = useState(false) + ({ placeholder = 'Select...', className, children, ...props }, ref) => { + const innerRef = useRef(null); + const [isPlaceholder, setIsPlaceholder] = useState(false); useImperativeHandle( ref, - () => innerRef.current - ) + () => innerRef.current, + ); useEffect(() => { - if (innerRef.current && innerRef.current.value === "") { - setIsPlaceholder(true) + if (innerRef.current && innerRef.current.value === '') { + setIsPlaceholder(true); } else { - setIsPlaceholder(false) + setIsPlaceholder(false); } - }, [innerRef.current?.value]) + }, [innerRef.current?.value]); return (
@@ -42,32 +42,32 @@ const CartItemSelect = forwardRef( onFocus={() => innerRef.current?.focus()} onBlur={() => innerRef.current?.blur()} className={clx( - "relative flex items-center txt-compact-small border text-ui-fg-base group", + 'txt-compact-small text-ui-fg-base group relative flex items-center border', className, { - "text-ui-fg-subtle": isPlaceholder, - } + 'text-ui-fg-subtle': isPlaceholder, + }, )} > - +
- ) - } -) + ); + }, +); -CartItemSelect.displayName = "CartItemSelect" +CartItemSelect.displayName = 'CartItemSelect'; -export default CartItemSelect +export default CartItemSelect; diff --git a/packages/features/medusa-storefront/src/modules/cart/components/empty-cart-message/index.tsx b/packages/features/medusa-storefront/src/modules/cart/components/empty-cart-message/index.tsx index e04a1f8..5bee4bd 100644 --- a/packages/features/medusa-storefront/src/modules/cart/components/empty-cart-message/index.tsx +++ b/packages/features/medusa-storefront/src/modules/cart/components/empty-cart-message/index.tsx @@ -1,13 +1,15 @@ -import { Heading, Text } from "@medusajs/ui" - -import InteractiveLink from "@modules/common/components/interactive-link" +import { Heading, Text } from '@medusajs/ui'; +import InteractiveLink from '@modules/common/components/interactive-link'; const EmptyCartMessage = () => { return ( -
+
Cart @@ -19,7 +21,7 @@ const EmptyCartMessage = () => { Explore products
- ) -} + ); +}; -export default EmptyCartMessage +export default EmptyCartMessage; diff --git a/packages/features/medusa-storefront/src/modules/cart/components/item/index.tsx b/packages/features/medusa-storefront/src/modules/cart/components/item/index.tsx index b35f1c4..df8abd3 100644 --- a/packages/features/medusa-storefront/src/modules/cart/components/item/index.tsx +++ b/packages/features/medusa-storefront/src/modules/cart/components/item/index.tsx @@ -1,57 +1,58 @@ -"use client" +'use client'; -import { Table, Text, clx } from "@medusajs/ui" -import { updateLineItem } from "@lib/data/cart" -import { HttpTypes } from "@medusajs/types" -import CartItemSelect from "@modules/cart/components/cart-item-select" -import ErrorMessage from "@modules/checkout/components/error-message" -import DeleteButton from "@modules/common/components/delete-button" -import LineItemOptions from "@modules/common/components/line-item-options" -import LineItemPrice from "@modules/common/components/line-item-price" -import LineItemUnitPrice from "@modules/common/components/line-item-unit-price" -import LocalizedClientLink from "@modules/common/components/localized-client-link" -import Spinner from "@modules/common/icons/spinner" -import Thumbnail from "@modules/products/components/thumbnail" -import { useState } from "react" +import { useState } from 'react'; + +import { updateLineItem } from '@lib/data/cart'; +import { HttpTypes } from '@medusajs/types'; +import { Table, Text, clx } from '@medusajs/ui'; +import CartItemSelect from '@modules/cart/components/cart-item-select'; +import ErrorMessage from '@modules/checkout/components/error-message'; +import DeleteButton from '@modules/common/components/delete-button'; +import LineItemOptions from '@modules/common/components/line-item-options'; +import LineItemPrice from '@modules/common/components/line-item-price'; +import LineItemUnitPrice from '@modules/common/components/line-item-unit-price'; +import LocalizedClientLink from '@modules/common/components/localized-client-link'; +import Spinner from '@modules/common/icons/spinner'; +import Thumbnail from '@modules/products/components/thumbnail'; type ItemProps = { - item: HttpTypes.StoreCartLineItem - type?: "full" | "preview" - currencyCode: string -} + item: HttpTypes.StoreCartLineItem; + type?: 'full' | 'preview'; + currencyCode: string; +}; -const Item = ({ item, type = "full", currencyCode }: ItemProps) => { - const [updating, setUpdating] = useState(false) - const [error, setError] = useState(null) +const Item = ({ item, type = 'full', currencyCode }: ItemProps) => { + const [updating, setUpdating] = useState(false); + const [error, setError] = useState(null); const changeQuantity = async (quantity: number) => { - setError(null) - setUpdating(true) + setError(null); + setUpdating(true); await updateLineItem({ lineId: item.id, quantity, }) .catch((err) => { - setError(err.message) + setError(err.message); }) .finally(() => { - setUpdating(false) - }) - } + setUpdating(false); + }); + }; // TODO: Update this to grab the actual max inventory - const maxQtyFromInventory = 10 - const maxQuantity = item.variant?.manage_inventory ? 10 : maxQtyFromInventory + const maxQtyFromInventory = 10; + const maxQuantity = item.variant?.manage_inventory ? 10 : maxQtyFromInventory; return ( - + { - {type === "full" && ( + {type === 'full' && ( -
+
changeQuantity(parseInt(value.target.value))} - className="w-14 h-10 p-4" + className="h-10 w-14 p-4" data-testid="product-select-button" > {/* TODO: Update this with the v2 way of managing inventory */} @@ -91,7 +92,7 @@ const Item = ({ item, type = "full", currencyCode }: ItemProps) => { - ) + ), )}