diff --git a/app/home/(user)/_components/dashboard.tsx b/app/home/(user)/_components/dashboard.tsx new file mode 100644 index 0000000..f6cc187 --- /dev/null +++ b/app/home/(user)/_components/dashboard.tsx @@ -0,0 +1,237 @@ +'use client'; + +import { InfoTooltip } from '@/components/ui/info-tooltip'; +import { toTitleCase } from '@/lib/utils'; +import { BlendingModeIcon, RulerHorizontalIcon } from '@radix-ui/react-icons'; +import { + Activity, + ChevronRight, + Clock9, + Droplets, + LineChart, + Pill, + Scale, + TrendingUp, + User, +} from 'lucide-react'; + +import { usePersonalAccountData } from '@kit/accounts/hooks/use-personal-account-data'; +import { useUserWorkspace } from '@kit/accounts/hooks/use-user-workspace'; +import { Button } from '@kit/ui/button'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardProps, +} from '@kit/ui/card'; +import { PageDescription } from '@kit/ui/page'; +import { Trans } from '@kit/ui/trans'; +import { cn } from '@kit/ui/utils'; + +const dummyCards = [ + { + title: 'dashboard:gender', + description: 'dashboard:male', + icon: , + iconBg: 'bg-success', + }, + { + title: 'dashboard:age', + description: '43', + icon: , + iconBg: 'bg-success', + }, + { + title: 'dashboard:height', + description: '183', + icon: , + iconBg: 'bg-success', + }, + { + title: 'dashboard:weight', + description: '92kg', + icon: , + iconBg: 'bg-warning', + }, + { + title: 'dashboard:bmi', + description: '27.5', + icon: , + iconBg: 'bg-warning', + }, + { + title: 'dashboard:bloodPressure', + description: '160/98', + icon: , + iconBg: 'bg-warning', + }, + { + title: 'dashboard:cholesterol', + description: '5', + icon: , + iconBg: 'bg-destructive', + }, + { + title: 'dashboard:ldlCholesterol', + description: '3,6', + icon: , + iconBg: 'bg-warning', + }, + { + title: 'Score 2', + description: 'Normis', + icon: , + iconBg: 'bg-success', + }, + { + title: 'dashboard:smoking', + description: 'dashboard:respondToQuestion', + descriptionColor: 'text-primary', + icon: ( + + ), + cardVariant: 'gradient-success' as CardProps['variant'], + }, +]; + +const dummyRecommendations = [ + { + icon: , + color: 'bg-cyan/10 text-cyan', + title: 'Kolesterooli kontroll', + description: 'HDL-kolestrool', + tooltipContent: 'Selgitus', + price: '20,00 €', + buttonText: 'Telli', + }, + { + icon: , + color: 'bg-primary/10 text-primary', + title: 'Kolesterooli kontroll', + tooltipContent: 'Selgitus', + description: 'LDL-Kolesterool', + buttonText: 'Broneeri', + }, + { + icon: , + color: 'bg-destructive/10 text-destructive', + title: 'Vererõhu kontroll', + tooltipContent: 'Selgitus', + description: 'Score-Risk 2', + price: '20,00 €', + buttonText: 'Telli', + }, +]; + +export default function Dashboard() { + const userWorkspace = useUserWorkspace(); + const account = usePersonalAccountData(userWorkspace.user.id); + + return ( + <> +
+

+ + {account?.data?.name ? `, ${toTitleCase(account.data.name)}` : ''} +

+ + : + +
+
+ {dummyCards.map( + ({ + title, + description, + icon, + iconBg, + cardVariant, + descriptionColor, + }) => ( + + +
+ {icon} +
+
+ +
+ +
+ + + +
+
+ ), + )} +
+ + +

+ +

+
+ + {dummyRecommendations.map( + ( + { + icon, + color, + title, + description, + tooltipContent, + price, + buttonText, + }, + index, + ) => { + return ( +
+
+
+ {icon} +
+
+
+ {title} + +
+

+ {description} +

+
+
+
+

{price}

+ +
+
+ ); + }, + )} +
+
+ + ); +} diff --git a/app/home/(user)/_components/home-menu-navigation.tsx b/app/home/(user)/_components/home-menu-navigation.tsx index d91e498..6f2db1a 100644 --- a/app/home/(user)/_components/home-menu-navigation.tsx +++ b/app/home/(user)/_components/home-menu-navigation.tsx @@ -1,65 +1,43 @@ -import { - BorderedNavigationMenu, - BorderedNavigationMenuItem, -} from '@kit/ui/bordered-navigation-menu'; -import { If } from '@kit/ui/if'; +import { ShoppingCart } from 'lucide-react'; + +import { Button } from '@kit/ui/button'; +import { Trans } from '@kit/ui/trans'; +import { cn } from '@kit/ui/utils'; import { AppLogo } from '~/components/app-logo'; import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container'; -import featuresFlagConfig from '~/config/feature-flags.config'; -import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config'; +import { Search } from '~/components/ui/search'; +import { SIDEBAR_WIDTH } from '../../../../packages/ui/src/shadcn/constants'; // home imports -import { HomeAccountSelector } from '../_components/home-account-selector'; import { UserNotifications } from '../_components/user-notifications'; import { type UserWorkspace } from '../_lib/server/load-user-workspace'; export function HomeMenuNavigation(props: { workspace: UserWorkspace }) { - const { workspace, user, accounts } = props.workspace; - - const routes = personalAccountNavigationConfig.routes.reduce< - Array<{ - path: string; - label: string; - Icon?: React.ReactNode; - end?: boolean | ((path: string) => boolean); - }> - >((acc, item) => { - if ('children' in item) { - return [...acc, ...item.children]; - } - - if ('divider' in item) { - return acc; - } - - return [...acc, item]; - }, []); + const { workspace, user } = props.workspace; return ( -
-
+
+
- - - {routes.map((route) => ( - - ))} -
+ } + /> -
+
+ - - - -
diff --git a/app/home/(user)/_components/home-sidebar.tsx b/app/home/(user)/_components/home-sidebar.tsx index e434119..e1e53b6 100644 --- a/app/home/(user)/_components/home-sidebar.tsx +++ b/app/home/(user)/_components/home-sidebar.tsx @@ -1,61 +1,29 @@ -import { If } from '@kit/ui/if'; import { Sidebar, SidebarContent, - SidebarFooter, SidebarHeader, SidebarNavigation, } from '@kit/ui/shadcn-sidebar'; -import { cn } from '@kit/ui/utils'; +import { Trans } from '@kit/ui/trans'; -import { AppLogo } from '~/components/app-logo'; -import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container'; -import featuresFlagConfig from '~/config/feature-flags.config'; import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config'; -import { UserNotifications } from '~/home/(user)/_components/user-notifications'; -// home imports -import type { UserWorkspace } from '../_lib/server/load-user-workspace'; -import { HomeAccountSelector } from './home-account-selector'; - -interface HomeSidebarProps { - workspace: UserWorkspace; -} - -export function HomeSidebar(props: HomeSidebarProps) { - const { workspace, user, accounts } = props.workspace; +export function HomeSidebar() { const collapsible = personalAccountNavigationConfig.sidebarCollapsedStyle; return ( - -
- - } - > - - - -
- -
+ +
+
+ +
- - - - ); } diff --git a/app/home/(user)/layout.tsx b/app/home/(user)/layout.tsx index 6ede447..97f38ae 100644 --- a/app/home/(user)/layout.tsx +++ b/app/home/(user)/layout.tsx @@ -39,7 +39,7 @@ function SidebarLayout({ children }: React.PropsWithChildren) { - + @@ -58,8 +58,8 @@ function HeaderLayout({ children }: React.PropsWithChildren) { return ( - - + + @@ -67,7 +67,14 @@ function HeaderLayout({ children }: React.PropsWithChildren) { - {children} + + + + + + {children} + + ); diff --git a/app/home/(user)/page.tsx b/app/home/(user)/page.tsx index 3327e1f..cc64e11 100644 --- a/app/home/(user)/page.tsx +++ b/app/home/(user)/page.tsx @@ -4,6 +4,7 @@ import { Trans } from '@kit/ui/trans'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; +import Dashboard from './_components/dashboard'; // local imports import { HomeLayoutPageHeader } from './_components/home-page-header'; @@ -21,10 +22,12 @@ function UserHomePage() { <> } - description={} + description={<>} /> - + + + ); } diff --git a/components/ui/info-tooltip.tsx b/components/ui/info-tooltip.tsx new file mode 100644 index 0000000..7883844 --- /dev/null +++ b/components/ui/info-tooltip.tsx @@ -0,0 +1,16 @@ +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@kit/ui/tooltip"; +import { Info } from "lucide-react"; + +export function InfoTooltip({ content }: { content?: string }) { + if (!content) return null; + return ( + + + + + + {content} + + + ); +} diff --git a/components/ui/search.tsx b/components/ui/search.tsx new file mode 100644 index 0000000..84dac99 --- /dev/null +++ b/components/ui/search.tsx @@ -0,0 +1,33 @@ +import React, { JSX, ReactNode } from 'react'; + +import { cn } from '@kit/ui/utils'; + +export type SearchProps = React.InputHTMLAttributes & { + startElement?: string | JSX.Element; + className?: string; +}; + +const Search = React.forwardRef( + ({ className, startElement, ...props }, ref) => { + return ( +
+ {!!startElement && startElement} + +
+ ); + }, +); + +Search.displayName = 'Search'; + +export { Search }; diff --git a/config/paths.config.ts b/config/paths.config.ts index 340e208..f4cce84 100644 --- a/config/paths.config.ts +++ b/config/paths.config.ts @@ -13,6 +13,12 @@ const PathsSchema = z.object({ }), app: z.object({ home: z.string().min(1), + booking: z.string().min(1), + myOrders: z.string().min(1), + analysisResults: z.string().min(1), + orderAnalysisPackage: z.string().min(1), + orderAnalysis: z.string().min(1), + orderHealthAnalysis: z.string().min(1), personalAccountSettings: z.string().min(1), personalAccountBilling: z.string().min(1), personalAccountBillingReturn: z.string().min(1), @@ -47,6 +53,13 @@ const pathsConfig = PathsSchema.parse({ accountMembers: `/home/[account]/members`, accountBillingReturn: `/home/[account]/billing/return`, joinTeam: '/join', + // these routes are added as placeholders and can be changed when the pages are added + booking: '/booking', + myOrders: '/my-orders', + analysisResults: '/analysis-results', + orderAnalysisPackage: '/order-analysis-package', + orderAnalysis: '/order-analysis', + orderHealthAnalysis: '/order-health-analysis' }, } satisfies z.infer); diff --git a/config/personal-account-navigation.config.tsx b/config/personal-account-navigation.config.tsx index d534f32..f52c78e 100644 --- a/config/personal-account-navigation.config.tsx +++ b/config/personal-account-navigation.config.tsx @@ -1,47 +1,72 @@ -import { CreditCard, Home, User } from 'lucide-react'; +import { + FileLineChart, + HeartPulse, + LineChart, + MousePointerClick, + ShoppingCart, + Stethoscope, + TestTube2, +} from 'lucide-react'; import { z } from 'zod'; import { NavigationConfigSchema } from '@kit/ui/navigation-schema'; -import featureFlagsConfig from '~/config/feature-flags.config'; import pathsConfig from '~/config/paths.config'; -const iconClasses = 'w-4'; +const iconClasses = 'w-4 stroke-[1.5px]'; const routes = [ { - label: 'common:routes.application', children: [ { - label: 'common:routes.home', + label: 'common:routes.overview', path: pathsConfig.app.home, - Icon: , + Icon: , + end: true, + }, + { + label: 'common:routes.booking', + path: pathsConfig.app.booking, + Icon: , + end: true, + }, + { + label: 'common:routes.myOrders', + path: pathsConfig.app.myOrders, + Icon: , + end: true, + }, + { + label: 'common:routes.analysisResults', + path: pathsConfig.app.analysisResults, + Icon: , + end: true, + }, + { + label: 'common:routes.orderAnalysisPackage', + path: pathsConfig.app.orderAnalysisPackage, + Icon: , + end: true, + }, + { + label: 'common:routes.orderAnalysis', + path: pathsConfig.app.orderAnalysis, + Icon: , + end: true, + }, + { + label: 'common:routes.orderHealthAnalysis', + path: pathsConfig.app.orderHealthAnalysis, + Icon: , end: true, }, ], }, - { - label: 'common:routes.settings', - children: [ - { - label: 'common:routes.profile', - path: pathsConfig.app.personalAccountSettings, - Icon: , - }, - featureFlagsConfig.enablePersonalAccountBilling - ? { - label: 'common:routes.billing', - path: pathsConfig.app.personalAccountBilling, - Icon: , - } - : undefined, - ].filter((route) => !!route), - }, ] satisfies z.infer['routes']; export const personalAccountNavigationConfig = NavigationConfigSchema.parse({ routes, - style: process.env.NEXT_PUBLIC_USER_NAVIGATION_STYLE, - sidebarCollapsed: process.env.NEXT_PUBLIC_HOME_SIDEBAR_COLLAPSED, - sidebarCollapsedStyle: process.env.NEXT_PUBLIC_SIDEBAR_COLLAPSED_STYLE, + style: 'custom', + sidebarCollapsed: false, + sidebarCollapsedStyle: 'icon', }); diff --git a/lib/i18n/i18n.settings.ts b/lib/i18n/i18n.settings.ts index bb70925..78308fa 100644 --- a/lib/i18n/i18n.settings.ts +++ b/lib/i18n/i18n.settings.ts @@ -32,6 +32,7 @@ export const defaultI18nNamespaces = [ 'teams', 'billing', 'marketing', + 'dashboard', ]; /** diff --git a/lib/utils.ts b/lib/utils.ts index 405dd01..1eb8901 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,5 +1,5 @@ -import { clsx, type ClassValue } from "clsx"; -import { twMerge } from "tailwind-merge"; +import { type ClassValue, clsx } from 'clsx'; +import { twMerge } from 'tailwind-merge'; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); @@ -9,3 +9,12 @@ export function toArray(input?: T | T[] | null): T[] { if (!input) return []; return Array.isArray(input) ? input : [input]; } + +export function toTitleCase(str?: string) { + if (!str) return ''; + return str.replace( + /\w\S*/g, + (text: string) => + text.charAt(0).toUpperCase() + text.substring(1).toLowerCase(), + ); +} diff --git a/packages/features/notifications/src/components/notifications-popover.tsx b/packages/features/notifications/src/components/notifications-popover.tsx index c2c0116..208351b 100644 --- a/packages/features/notifications/src/components/notifications-popover.tsx +++ b/packages/features/notifications/src/components/notifications-popover.tsx @@ -121,12 +121,12 @@ export function NotificationsPopover(params: { return ( -