B2B-36: add personal dashboard (#20)

* B2B-36: add dashboard cards

* B2B-36: add dashboard cards

* card variants, some improvements, gen db types

* add menus to home page

* update db types

* remove unnecessary card variant

---------

Co-authored-by: Helena <helena@Helenas-MacBook-Pro.local>
This commit is contained in:
Helena
2025-06-30 21:11:18 +03:00
committed by GitHub
parent d7841ca6ba
commit 297dd7c221
28 changed files with 1016 additions and 261 deletions

View File

@@ -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: <User />,
iconBg: 'bg-success',
},
{
title: 'dashboard:age',
description: '43',
icon: <Clock9 />,
iconBg: 'bg-success',
},
{
title: 'dashboard:height',
description: '183',
icon: <RulerHorizontalIcon className="size-4" />,
iconBg: 'bg-success',
},
{
title: 'dashboard:weight',
description: '92kg',
icon: <Scale />,
iconBg: 'bg-warning',
},
{
title: 'dashboard:bmi',
description: '27.5',
icon: <TrendingUp />,
iconBg: 'bg-warning',
},
{
title: 'dashboard:bloodPressure',
description: '160/98',
icon: <Activity />,
iconBg: 'bg-warning',
},
{
title: 'dashboard:cholesterol',
description: '5',
icon: <BlendingModeIcon className="size-4" />,
iconBg: 'bg-destructive',
},
{
title: 'dashboard:ldlCholesterol',
description: '3,6',
icon: <Pill />,
iconBg: 'bg-warning',
},
{
title: 'Score 2',
description: 'Normis',
icon: <LineChart />,
iconBg: 'bg-success',
},
{
title: 'dashboard:smoking',
description: 'dashboard:respondToQuestion',
descriptionColor: 'text-primary',
icon: (
<Button size="icon" variant="outline" className="px-2 text-black">
<ChevronRight className="size-4 stroke-2" />
</Button>
),
cardVariant: 'gradient-success' as CardProps['variant'],
},
];
const dummyRecommendations = [
{
icon: <BlendingModeIcon className="size-4" />,
color: 'bg-cyan/10 text-cyan',
title: 'Kolesterooli kontroll',
description: 'HDL-kolestrool',
tooltipContent: 'Selgitus',
price: '20,00 €',
buttonText: 'Telli',
},
{
icon: <BlendingModeIcon className="size-4" />,
color: 'bg-primary/10 text-primary',
title: 'Kolesterooli kontroll',
tooltipContent: 'Selgitus',
description: 'LDL-Kolesterool',
buttonText: 'Broneeri',
},
{
icon: <Droplets />,
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 (
<>
<div>
<h4>
<Trans i18nKey={'common:welcome'} />
{account?.data?.name ? `, ${toTitleCase(account.data.name)}` : ''}
</h4>
<PageDescription>
<Trans i18nKey={'dashboard:recentlyCheckedDescription'} />:
</PageDescription>
</div>
<div className="grid auto-rows-fr grid-cols-5 gap-3">
{dummyCards.map(
({
title,
description,
icon,
iconBg,
cardVariant,
descriptionColor,
}) => (
<Card
key={title}
variant={cardVariant}
className="flex flex-col justify-between"
>
<CardHeader className="items-end-safe">
<div
className={cn(
'flex size-8 items-center-safe justify-center-safe rounded-full text-white',
iconBg,
)}
>
{icon}
</div>
</CardHeader>
<CardFooter className="flex flex-col items-start">
<h5>
<Trans i18nKey={title} />
</h5>
<CardDescription className={descriptionColor}>
<Trans i18nKey={description} />
</CardDescription>
</CardFooter>
</Card>
),
)}
</div>
<Card>
<CardHeader className="items-start">
<h4>
<Trans i18nKey="dashboard:recommendedForYou" />
</h4>
</CardHeader>
<CardContent className="space-y-6">
{dummyRecommendations.map(
(
{
icon,
color,
title,
description,
tooltipContent,
price,
buttonText,
},
index,
) => {
return (
<div className="flex justify-between" key={index}>
<div className="flex flex-row items-center gap-4">
<div
className={cn(
'flex size-8 items-center-safe justify-center-safe rounded-full text-white',
color,
)}
>
{icon}
</div>
<div>
<div className="inline-flex items-center gap-1 align-baseline text-sm font-medium">
{title}
<InfoTooltip content={tooltipContent} />
</div>
<p className="text-muted-foreground text-sm">
{description}
</p>
</div>
</div>
<div className="grid w-36 auto-rows-fr grid-cols-2 items-center gap-4">
<p className="text-sm font-medium"> {price}</p>
<Button size="sm" variant="secondary">
{buttonText}
</Button>
</div>
</div>
);
},
)}
</CardContent>
</Card>
</>
);
}

View File

@@ -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 (
<div className={'flex w-full flex-1 justify-between'}>
<div className={'flex items-center space-x-8'}>
<div className={'flex w-full flex-1 items-center justify-between gap-3'}>
<div className={cn('flex items-center', `w-[${SIDEBAR_WIDTH}]`)}>
<AppLogo />
<BorderedNavigationMenu>
{routes.map((route) => (
<BorderedNavigationMenuItem {...route} key={route.path} />
))}
</BorderedNavigationMenu>
</div>
<Search
className="flex grow"
startElement={<Trans i18nKey="common:search" values={{ end: '...' }} />}
/>
<div className={'flex justify-end space-x-2.5'}>
<div className="flex items-center justify-end gap-3">
<Button variant="outline">
<ShoppingCart className="stroke-[1.5px]" />
<Trans i18nKey="common:shoppingCart" /> (0)
</Button>
<UserNotifications userId={user.id} />
<If condition={featuresFlagConfig.enableTeamAccounts}>
<HomeAccountSelector userId={user.id} accounts={accounts} />
</If>
<div>
<ProfileAccountDropdownContainer
user={user}
account={workspace}
showProfileName={false}
showProfileName
/>
</div>
</div>

View File

@@ -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 (
<Sidebar collapsible={collapsible}>
<SidebarHeader className={'h-16 justify-center'}>
<div className={'flex items-center justify-between gap-x-3'}>
<If
condition={featuresFlagConfig.enableTeamAccounts}
fallback={
<AppLogo
className={cn(
'p-2 group-data-[minimized=true]:max-w-full group-data-[minimized=true]:py-0',
)}
/>
}
>
<HomeAccountSelector userId={user.id} accounts={accounts} />
</If>
<div className={'group-data-[minimized=true]:hidden'}>
<UserNotifications userId={user.id} />
</div>
<SidebarHeader className="h-24 justify-center">
<div className="mt-24 flex items-center">
<h5>
<Trans i18nKey="common:myActions" />
</h5>
</div>
</SidebarHeader>
<SidebarContent>
<SidebarNavigation config={personalAccountNavigationConfig} />
</SidebarContent>
<SidebarFooter>
<ProfileAccountDropdownContainer user={user} account={workspace} />
</SidebarFooter>
</Sidebar>
);
}

View File

@@ -39,7 +39,7 @@ function SidebarLayout({ children }: React.PropsWithChildren) {
<SidebarProvider defaultOpen={state.open}>
<Page style={'sidebar'}>
<PageNavigation>
<HomeSidebar workspace={workspace} />
<HomeSidebar />
</PageNavigation>
<PageMobileNavigation className={'flex items-center justify-between'}>
@@ -58,8 +58,8 @@ function HeaderLayout({ children }: React.PropsWithChildren) {
return (
<UserWorkspaceContextProvider value={workspace}>
<Page style={'header'}>
<PageNavigation>
<Page style={'header'} >
<PageNavigation >
<HomeMenuNavigation workspace={workspace} />
</PageNavigation>
@@ -67,7 +67,14 @@ function HeaderLayout({ children }: React.PropsWithChildren) {
<MobileNavigation workspace={workspace} />
</PageMobileNavigation>
{children}
<SidebarProvider defaultOpen>
<Page style={'sidebar'}>
<PageNavigation>
<HomeSidebar />
</PageNavigation>
{children}
</Page>
</SidebarProvider>
</Page>
</UserWorkspaceContextProvider>
);

View File

@@ -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() {
<>
<HomeLayoutPageHeader
title={<Trans i18nKey={'common:routes.home'} />}
description={<Trans i18nKey={'common:homeTabDescription'} />}
description={<></>}
/>
<PageBody></PageBody>
<PageBody>
<Dashboard />
</PageBody>
</>
);
}

View File

@@ -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 (
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Info className="size-4 cursor-pointer" />
</TooltipTrigger>
<TooltipContent>{content}</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}

33
components/ui/search.tsx Normal file
View File

@@ -0,0 +1,33 @@
import React, { JSX, ReactNode } from 'react';
import { cn } from '@kit/ui/utils';
export type SearchProps = React.InputHTMLAttributes<HTMLInputElement> & {
startElement?: string | JSX.Element;
className?: string;
};
const Search = React.forwardRef<HTMLInputElement, SearchProps>(
({ className, startElement, ...props }, ref) => {
return (
<div
className={cn(
'border-input ring-offset-background focus-within:ring-ring flex h-10 items-center rounded-md border bg-white pl-3 text-sm focus-within:ring-1 focus-within:ring-offset-2',
className,
)}
>
{!!startElement && startElement}
<input
{...props}
type="search"
ref={ref}
className="placeholder:text-muted-foreground w-full p-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
/>
</div>
);
},
);
Search.displayName = 'Search';
export { Search };

View File

@@ -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<typeof PathsSchema>);

View File

@@ -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: <Home className={iconClasses} />,
Icon: <LineChart className={iconClasses} />,
end: true,
},
{
label: 'common:routes.booking',
path: pathsConfig.app.booking,
Icon: <MousePointerClick className={iconClasses} />,
end: true,
},
{
label: 'common:routes.myOrders',
path: pathsConfig.app.myOrders,
Icon: <ShoppingCart className={iconClasses} />,
end: true,
},
{
label: 'common:routes.analysisResults',
path: pathsConfig.app.analysisResults,
Icon: <TestTube2 className={iconClasses} />,
end: true,
},
{
label: 'common:routes.orderAnalysisPackage',
path: pathsConfig.app.orderAnalysisPackage,
Icon: <HeartPulse className={iconClasses} />,
end: true,
},
{
label: 'common:routes.orderAnalysis',
path: pathsConfig.app.orderAnalysis,
Icon: <FileLineChart className={iconClasses} />,
end: true,
},
{
label: 'common:routes.orderHealthAnalysis',
path: pathsConfig.app.orderHealthAnalysis,
Icon: <Stethoscope className={iconClasses} />,
end: true,
},
],
},
{
label: 'common:routes.settings',
children: [
{
label: 'common:routes.profile',
path: pathsConfig.app.personalAccountSettings,
Icon: <User className={iconClasses} />,
},
featureFlagsConfig.enablePersonalAccountBilling
? {
label: 'common:routes.billing',
path: pathsConfig.app.personalAccountBilling,
Icon: <CreditCard className={iconClasses} />,
}
: undefined,
].filter((route) => !!route),
},
] satisfies z.infer<typeof NavigationConfigSchema>['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',
});

View File

@@ -32,6 +32,7 @@ export const defaultI18nNamespaces = [
'teams',
'billing',
'marketing',
'dashboard',
];
/**

View File

@@ -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<T>(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(),
);
}

View File

@@ -121,12 +121,12 @@ export function NotificationsPopover(params: {
return (
<Popover modal open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button className={'relative h-9 w-9'} variant={'ghost'}>
<Bell className={'min-h-4 min-w-4'} />
<Button className="relative w-5" variant={'outline'}>
<Bell className={'min-h-4 min-w-4 stroke-[1.5px]'} />
<span
className={cn(
`fade-in animate-in zoom-in absolute right-1 top-1 mt-0 flex h-3.5 w-3.5 items-center justify-center rounded-full bg-red-500 text-[0.65rem] text-white`,
`fade-in animate-in zoom-in absolute top-1 right-1 mt-0 flex h-3.5 w-3.5 items-center justify-center rounded-full bg-red-500 text-[0.65rem] text-white`,
{
hidden: !notifications.length,
},
@@ -186,7 +186,7 @@ export function NotificationsPopover(params: {
<div
key={notification.id.toString()}
className={cn(
'min-h-18 flex flex-col items-start justify-center gap-y-1 px-3 py-2',
'flex min-h-18 flex-col items-start justify-center gap-y-1 px-3 py-2',
)}
onClick={() => {
if (params.onClick) {

View File

@@ -48,6 +48,48 @@ export type Database = {
}
Relationships: []
}
request_entries: {
Row: {
comment: string | null
created_at: string
id: number
personal_code: number | null
request_api: string
request_api_method: string
requested_end_date: string | null
requested_start_date: string | null
service_id: number | null
service_provider_id: number | null
status: Database["audit"]["Enums"]["request_status"]
}
Insert: {
comment?: string | null
created_at?: string
id?: number
personal_code?: number | null
request_api: string
request_api_method: string
requested_end_date?: string | null
requested_start_date?: string | null
service_id?: number | null
service_provider_id?: number | null
status: Database["audit"]["Enums"]["request_status"]
}
Update: {
comment?: string | null
created_at?: string
id?: number
personal_code?: number | null
request_api?: string
request_api_method?: string
requested_end_date?: string | null
requested_start_date?: string | null
service_id?: number | null
service_provider_id?: number | null
status?: Database["audit"]["Enums"]["request_status"]
}
Relationships: []
}
sync_entries: {
Row: {
changed_by_role: string
@@ -83,6 +125,7 @@ export type Database = {
[_ in never]: never
}
Enums: {
request_status: "SUCCESS" | "FAIL"
sync_status: "SUCCESS" | "FAIL"
}
CompositeTypes: {
@@ -635,6 +678,158 @@ export type Database = {
}
Relationships: []
}
connected_online_providers: {
Row: {
can_select_worker: boolean
created_at: string
email: string | null
id: number
name: string
personal_code_required: boolean
phone_number: string | null
updated_at: string | null
}
Insert: {
can_select_worker: boolean
created_at?: string
email?: string | null
id: number
name: string
personal_code_required: boolean
phone_number?: string | null
updated_at?: string | null
}
Update: {
can_select_worker?: boolean
created_at?: string
email?: string | null
id?: number
name?: string
personal_code_required?: boolean
phone_number?: string | null
updated_at?: string | null
}
Relationships: []
}
connected_online_reservation: {
Row: {
booking_code: string
clinic_id: number
comments: string | null
created_at: string
discount_code: string | null
id: number
lang: string
requires_payment: boolean
service_id: number
service_user_id: number | null
start_time: string
sync_user_id: number
updated_at: string | null
user_id: string
}
Insert: {
booking_code: string
clinic_id: number
comments?: string | null
created_at?: string
discount_code?: string | null
id?: number
lang: string
requires_payment: boolean
service_id: number
service_user_id?: number | null
start_time: string
sync_user_id: number
updated_at?: string | null
user_id: string
}
Update: {
booking_code?: string
clinic_id?: number
comments?: string | null
created_at?: string
discount_code?: string | null
id?: number
lang?: string
requires_payment?: boolean
service_id?: number
service_user_id?: number | null
start_time?: string
sync_user_id?: number
updated_at?: string | null
user_id?: string
}
Relationships: []
}
connected_online_services: {
Row: {
clinic_id: number
code: string
created_at: string
description: string | null
display: string | null
duration: number
has_free_codes: boolean
id: number
name: string
neto_duration: number | null
online_hide_duration: number | null
online_hide_price: number | null
price: number
price_periods: string | null
requires_payment: boolean
sync_id: number
updated_at: string | null
}
Insert: {
clinic_id: number
code: string
created_at?: string
description?: string | null
display?: string | null
duration: number
has_free_codes: boolean
id: number
name: string
neto_duration?: number | null
online_hide_duration?: number | null
online_hide_price?: number | null
price: number
price_periods?: string | null
requires_payment: boolean
sync_id: number
updated_at?: string | null
}
Update: {
clinic_id?: number
code?: string
created_at?: string
description?: string | null
display?: string | null
duration?: number
has_free_codes?: boolean
id?: number
name?: string
neto_duration?: number | null
online_hide_duration?: number | null
online_hide_price?: number | null
price?: number
price_periods?: string | null
requires_payment?: boolean
sync_id?: number
updated_at?: string | null
}
Relationships: [
{
foreignKeyName: "connected_online_services_clinic_id_fkey"
columns: ["clinic_id"]
isOneToOne: false
referencedRelation: "connected_online_providers"
referencedColumns: ["id"]
},
]
}
invitations: {
Row: {
account_id: string
@@ -700,6 +895,129 @@ export type Database = {
},
]
}
medreport_product_groups: {
Row: {
created_at: string
id: number
name: string
updated_at: string | null
}
Insert: {
created_at?: string
id?: number
name: string
updated_at?: string | null
}
Update: {
created_at?: string
id?: number
name?: string
updated_at?: string | null
}
Relationships: []
}
medreport_products: {
Row: {
created_at: string
id: number
name: string
product_group_id: number | null
updated_at: string | null
}
Insert: {
created_at?: string
id?: number
name: string
product_group_id?: number | null
updated_at?: string | null
}
Update: {
created_at?: string
id?: number
name?: string
product_group_id?: number | null
updated_at?: string | null
}
Relationships: [
{
foreignKeyName: "medreport_products_product_groups_id_fkey"
columns: ["product_group_id"]
isOneToOne: false
referencedRelation: "medreport_product_groups"
referencedColumns: ["id"]
},
]
}
medreport_products_analyses_relations: {
Row: {
analysis_element_id: number | null
analysis_id: number | null
product_id: number
}
Insert: {
analysis_element_id?: number | null
analysis_id?: number | null
product_id: number
}
Update: {
analysis_element_id?: number | null
analysis_id?: number | null
product_id?: number
}
Relationships: [
{
foreignKeyName: "medreport_products_analyses_analysis_element_id_fkey"
columns: ["analysis_element_id"]
isOneToOne: true
referencedRelation: "analysis_elements"
referencedColumns: ["id"]
},
{
foreignKeyName: "medreport_products_analyses_analysis_id_fkey"
columns: ["analysis_id"]
isOneToOne: true
referencedRelation: "analyses"
referencedColumns: ["id"]
},
{
foreignKeyName: "medreport_products_analyses_product_id_fkey"
columns: ["product_id"]
isOneToOne: true
referencedRelation: "medreport_products"
referencedColumns: ["id"]
},
]
}
medreport_products_external_services_relations: {
Row: {
connected_online_service_id: number
product_id: number
}
Insert: {
connected_online_service_id: number
product_id: number
}
Update: {
connected_online_service_id?: number
product_id?: number
}
Relationships: [
{
foreignKeyName: "medreport_products_connected_online_services_id_fkey"
columns: ["connected_online_service_id"]
isOneToOne: true
referencedRelation: "connected_online_services"
referencedColumns: ["id"]
},
{
foreignKeyName: "medreport_products_connected_online_services_product_id_fkey"
columns: ["product_id"]
isOneToOne: false
referencedRelation: "medreport_products"
referencedColumns: ["id"]
},
]
}
nonces: {
Row: {
client_token: string
@@ -1543,6 +1861,7 @@ export type CompositeTypes<
export const Constants = {
audit: {
Enums: {
request_status: ["SUCCESS", "FAIL"],
sync_status: ["SUCCESS", "FAIL"],
},
},

View File

@@ -29,7 +29,7 @@ const RouteChild = z.object({
});
const RouteGroup = z.object({
label: z.string(),
label: z.string().optional(),
collapsible: z.boolean().optional(),
collapsed: z.boolean().optional(),
children: z.array(RouteChild),
@@ -37,12 +37,8 @@ const RouteGroup = z.object({
});
export const NavigationConfigSchema = z.object({
style: z.enum(['custom', 'sidebar', 'header']).default('sidebar'),
sidebarCollapsed: z
.enum(['false', 'true'])
.default('true')
.optional()
.transform((value) => value === `true`),
style: z.enum(['custom', 'sidebar', 'header']).default('custom'),
sidebarCollapsed: z.boolean().optional(),
sidebarCollapsedStyle: z.enum(['offcanvas', 'icon', 'none']).default('icon'),
routes: z.array(z.union([RouteGroup, Divider])),
});

View File

@@ -14,10 +14,6 @@ type PageProps = React.PropsWithChildren<{
sticky?: boolean;
}>;
const ENABLE_SIDEBAR_TRIGGER = process.env.NEXT_PUBLIC_ENABLE_SIDEBAR_TRIGGER
? process.env.NEXT_PUBLIC_ENABLE_SIDEBAR_TRIGGER === 'true'
: true;
export function Page(props: PageProps) {
switch (props.style) {
case 'header':
@@ -79,7 +75,7 @@ function PageWithHeader(props: PageProps) {
const { Navigation, Children, MobileNavigation } = getSlotsFromPage(props);
return (
<div className={cn('flex h-screen flex-1 flex-col', props.className)}>
<div className={cn('flex h-screen flex-1 flex-col z-1000', props.className)}>
<div
className={
props.contentContainerClassName ?? 'flex flex-1 flex-col space-y-4'
@@ -87,9 +83,9 @@ function PageWithHeader(props: PageProps) {
>
<div
className={cn(
'bg-muted/40 dark:border-border dark:shadow-primary/10 flex h-14 items-center justify-between px-4 lg:justify-start lg:shadow-xs',
'bg-muted/40 dark:border-border dark:shadow-primary/10 flex h-14 items-center justify-between px-4 lg:justify-start lg:shadow-xs border-b',
{
'sticky top-0 z-10 backdrop-blur-md': props.sticky ?? true,
'sticky top-0 z-1000 backdrop-blur-md': props.sticky ?? true,
},
)}
>
@@ -113,7 +109,10 @@ export function PageBody(
className?: string;
}>,
) {
const className = cn('flex w-full flex-1 flex-col lg:px-4', props.className);
const className = cn(
'flex w-full flex-1 flex-col space-y-6 lg:px-4',
props.className,
);
return <div className={className}>{props.children}</div>;
}
@@ -125,7 +124,7 @@ export function PageNavigation(props: React.PropsWithChildren) {
export function PageDescription(props: React.PropsWithChildren) {
return (
<div className={'flex h-6 items-center'}>
<div className={'text-muted-foreground text-xs leading-none font-normal'}>
<div className={'text-muted-foreground text-sm leading-none font-normal'}>
{props.children}
</div>
</div>
@@ -153,7 +152,7 @@ export function PageHeader({
title,
description,
className,
displaySidebarTrigger = ENABLE_SIDEBAR_TRIGGER,
displaySidebarTrigger = false,
}: React.PropsWithChildren<{
className?: string;
title?: string | React.ReactNode;

View File

@@ -7,7 +7,7 @@ import type { VariantProps } from 'class-variance-authority';
import { cn } from '../lib/utils';
const buttonVariants = cva(
'focus-visible:ring-ring inline-flex items-center justify-center rounded-md text-sm font-medium whitespace-nowrap transition-colors focus-visible:ring-1 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50',
'focus-visible:ring-ring gap-1 inline-flex items-center justify-center rounded-md text-sm font-medium whitespace-nowrap transition-colors focus-visible:ring-1 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {

View File

@@ -1,15 +1,31 @@
import * as React from 'react';
import { cn } from '../lib/utils';
import { VariantProps, cva } from 'class-variance-authority';
import { cn } from '.';
const Card: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
className,
...props
}) => (
<div
className={cn('bg-card text-card-foreground rounded-xl border', className)}
{...props}
/>
const cardVariants = cva('text-card-foreground rounded-xl border', {
variants: {
variant: {
default: 'bg-card',
'gradient-warning':
'from-warning/30 via-warning/10 to-background bg-gradient-to-t',
'gradient-destructive':
'from-destructive/30 via-destructive/10 to-background bg-gradient-to-t',
'gradient-success':
'from-success/30 via-success/10 to-background bg-gradient-to-t',
},
},
defaultVariants: {
variant: 'default',
},
});
export interface CardProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof cardVariants> {}
const Card: React.FC<CardProps> = ({ className, variant, ...props }) => (
<div className={cn(cardVariants({ variant, className }))} {...props} />
);
Card.displayName = 'Card';

View File

@@ -0,0 +1,3 @@
export const SIDEBAR_WIDTH = '16rem';
export const SIDEBAR_WIDTH_MOBILE = '18rem';
export const SIDEBAR_WIDTH_ICON = '4rem';

View File

@@ -21,6 +21,11 @@ import {
CollapsibleContent,
CollapsibleTrigger,
} from './collapsible';
import {
SIDEBAR_WIDTH,
SIDEBAR_WIDTH_ICON,
SIDEBAR_WIDTH_MOBILE,
} from './constants';
import { Input } from './input';
import { Separator } from './separator';
import { Sheet, SheetContent } from './sheet';
@@ -34,9 +39,6 @@ import {
const SIDEBAR_COOKIE_NAME = 'sidebar:state';
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
const SIDEBAR_WIDTH = '16rem';
const SIDEBAR_WIDTH_MOBILE = '18rem';
const SIDEBAR_WIDTH_ICON = '4rem';
const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
const SIDEBAR_MINIMIZED_WIDTH = SIDEBAR_WIDTH_ICON;
@@ -276,7 +278,7 @@ const Sidebar: React.FC<
<div
data-sidebar="sidebar"
className={cn(
'bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm',
'bg-sidebar group-data-[variant=floating]:border-sidebar-border ml-3 flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm',
{
'bg-transparent': variant === 'ghost',
},
@@ -908,7 +910,7 @@ export function SidebarNavigation({
tooltip={child.label}
>
<Link
className={cn('flex items-center', {
className={cn('flex items-center font-medium', {
'mx-auto w-full gap-0! [&>svg]:flex-1': !open,
})}
href={path}
@@ -916,7 +918,7 @@ export function SidebarNavigation({
{child.Icon}
<span
className={cn(
'w-auto transition-opacity duration-300',
'text-md w-auto font-medium transition-opacity duration-300',
{
'w-0 opacity-0': !open,
},

View File

@@ -55,8 +55,19 @@
"newVersionAvailableDescription": "A new version of the app is available. It is recommended to refresh the page to get the latest updates and avoid any issues.",
"newVersionSubmitButton": "Reload and Update",
"back": "Back",
"welcome": "Welcome",
"shoppingCart": "Shopping cart",
"search": "Search{{end}}",
"myActions": "My actions",
"routes": {
"home": "Home",
"overview": "Overview",
"booking": "Booking",
"myOrders": "My orders",
"analysisResults": "Analysis results",
"orderAnalysisPackage": "Telli analüüside pakett",
"orderAnalysis": "Order analysis",
"orderHealthAnalysis": "Telli terviseuuring",
"account": "Account",
"members": "Employees",
"billing": "Billing",
@@ -93,4 +104,4 @@
"reject": "Reject",
"accept": "Accept"
}
}
}

View File

@@ -0,0 +1,16 @@
{
"recentlyCheckedDescription": "Super, oled käinud tervist kontrollimas. Siin on sinule olulised näitajad",
"respondToQuestion": "Respond",
"gender": "Gender",
"male": "Male",
"female": "Female",
"age": "Age",
"height": "Height",
"weight": "Weight",
"bmi": "BMI",
"bloodPressure": "Blood pressure",
"cholesterol": "Cholesterol",
"ldlCholesterol": "LDL Cholesterol",
"smoking": "Smoking",
"recommendedForYou": "Recommended for you"
}

View File

@@ -54,8 +54,20 @@
"newVersionAvailable": "New version available",
"newVersionAvailableDescription": "A new version of the app is available. It is recommended to refresh the page to get the latest updates and avoid any issues.",
"newVersionSubmitButton": "Reload and Update",
"back": "Back",
"welcome": "Tere tulemast",
"shoppingCart": "Ostukorv",
"search": "Otsi{{end}}",
"myActions": "Minu toimingud",
"routes": {
"home": "Home",
"overview": "Ülevaade",
"booking": "Broneeri aeg",
"myOrders": "Minu tellimused",
"analysisResults": "Analüüside vastused",
"orderAnalysisPackage": "Telli analüüside pakett",
"orderAnalysis": "Telli analüüs",
"orderHealthAnalysis": "Telli terviseuuring",
"account": "Account",
"members": "Employees",
"billing": "Billing",
@@ -104,4 +116,4 @@
"weight": "Kaal",
"height": "Pikkus"
}
}
}

View File

@@ -0,0 +1,16 @@
{
"recentlyCheckedDescription": "Super, oled käinud tervist kontrollimas. Siin on sinule olulised näitajad",
"respondToQuestion": "Vasta küsimusele",
"gender": "Sugu",
"male": "Mees",
"female": "Naine",
"age": "Vanus",
"height": "Pikkus",
"weight": "Kaal",
"bmi": "KMI",
"bloodPressure": "Vererõhk",
"cholesterol": "Kolesterool",
"ldlCholesterol": "LDL kolesterool",
"smoking": "Suitsetamine",
"recommendedForYou": "Soovitused sulle"
}

View File

@@ -55,8 +55,18 @@
"newVersionAvailableDescription": "A new version of the app is available. It is recommended to refresh the page to get the latest updates and avoid any issues.",
"newVersionSubmitButton": "Reload and Update",
"back": "Back",
"welcome": "Welcome",
"shoppingCart": "Shopping cart",
"search": "Search{{end}}",
"myActions": "My actions",
"routes": {
"home": "Home",
"overview": "Overview",
"booking": "Booking",
"myOrders": "My orders",
"orderAnalysis": "Order analysis",
"orderAnalysisPackage": "Order analysis package",
"orderHealthAnalysis": "Order health analysis",
"account": "Account",
"members": "Employees",
"billing": "Billing",
@@ -93,4 +103,4 @@
"reject": "Reject",
"accept": "Accept"
}
}
}

View File

@@ -0,0 +1,16 @@
{
"recentlyCheckedDescription": "Super, oled käinud tervist kontrollimas. Siin on sinule olulised näitajad",
"respondToQuestion": "Respond",
"gender": "Gender",
"male": "Male",
"female": "Female",
"age": "Age",
"height": "Height",
"weight": "Weight",
"bmi": "BMI",
"bloodPressure": "Blood pressure",
"cholesterol": "Cholesterol",
"ldlCholesterol": "LDL Cholesterol",
"smoking": "Smoking",
"recommendedForYou": "Recommended for you"
}

View File

@@ -31,7 +31,9 @@
@layer base {
body {
@apply bg-background text-foreground;
font-feature-settings: "rlig" 1, "calt" 1;
font-feature-settings:
'rlig' 1,
'calt' 1;
}
*,
@@ -47,9 +49,44 @@
color: theme(--color-muted-foreground);
}
h1,
h2,
h3,
h4,
h5,
h6 {
@apply font-heading text-foreground font-semibold tracking-tight;
}
h1,h2,h3,h4,h5,h6 {
@apply font-heading text-foreground text-2xl font-semibold tracking-tight
}
h1 {
@apply text-5xl;
}
}
h2 {
@apply text-4xl;
}
h3 {
@apply text-3xl;
}
h4 {
@apply text-2xl;
}
h5 {
@apply text-xl;
}
h6 {
@apply text-lg;
}
.lucide {
stroke-width: 1;
}
.lucide {
@apply size-4;
}
}

View File

@@ -7,127 +7,130 @@
*/
@layer base {
:root {
--font-sans: var(--font-sans) -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
--font-heading: var(--font-heading);
:root {
--font-sans:
var(--font-sans) -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol';
--font-heading: var(--font-heading);
--background: hsla(0, 0%, 100%, 1);
--foreground: hsla(240, 10%, 4%, 1);
--foreground-50: hsla(240, 10%, 4%, 0.5);
--background: hsla(0, 0%, 100%, 1);
--foreground: hsla(240, 10%, 4%, 1);
--foreground-50: hsla(240, 10%, 4%, 0.5);
--background-90: hsla(0, 0%, 100%, 0.9);
--background-80: hsla(0, 0%, 100%, 0.8);
--background-90: hsla(0, 0%, 100%, 0.9);
--background-80: hsla(0, 0%, 100%, 0.8);
--card: var(--color-white);
--card-foreground: var(--color-neutral-950);
--card: var(--color-white);
--card-foreground: var(--color-neutral-950);
--popover: hsla(0, 0%, 100%, 1);
--popover-foreground: hsla(240, 10%, 4%, 1);
--popover: hsla(0, 0%, 100%, 1);
--popover-foreground: hsla(240, 10%, 4%, 1);
--primary: hsla(145, 78%, 18%, 1);
--primary-foreground: hsla(356, 100%, 97%, 1);
--primary: hsla(145, 78%, 18%, 1);
--primary-foreground: hsla(356, 100%, 97%, 1);
--primary-90: hsla(145, 78%, 18%, 0.9);
--primary-80: hsla(145, 78%, 18%, 0.8);
--primary-50: hsla(145, 78%, 18%, 0.5);
--primary-20: hsla(145, 78%, 18%, 0.2);
--primary-10: hsla(145, 78%, 18%, 0.1);
--primary-90: hsla(145, 78%, 18%, 0.9);
--primary-80: hsla(145, 78%, 18%, 0.8);
--primary-50: hsla(145, 78%, 18%, 0.5);
--primary-20: hsla(145, 78%, 18%, 0.2);
--primary-10: hsla(145, 78%, 18%, 0.1);
--secondary: hsla(240, 5%, 96%, 1);
--secondary-foreground: hsla(240, 6%, 10%, 1);
--secondary-90: hsla(240, 5%, 96%, 0.9);
--secondary-80: hsla(240, 5%, 96%, 0.8);
--secondary: hsla(240, 5%, 96%, 1);
--secondary-foreground: hsla(240, 6%, 10%, 1);
--secondary-90: hsla(240, 5%, 96%, 0.9);
--secondary-80: hsla(240, 5%, 96%, 0.8);
--muted: hsla(240, 5%, 96%, 1);
--muted-foreground: hsla(240, 4%, 41%, 1);
--muted: hsla(240, 5%, 96%, 1);
--muted-foreground: hsla(240, 4%, 41%, 1);
--muted-90: hsla(240, 5%, 96%, 0.9);
--muted-80: hsla(240, 5%, 96%, 0.8);
--muted-50: hsla(240, 5%, 96%, 0.5);
--muted-40: hsla(240, 5%, 96%, 0.4);
--muted-90: hsla(240, 5%, 96%, 0.9);
--muted-80: hsla(240, 5%, 96%, 0.8);
--muted-50: hsla(240, 5%, 96%, 0.5);
--muted-40: hsla(240, 5%, 96%, 0.4);
--accent: hsla(240, 5%, 96%, 1);
--accent-foreground: hsla(240, 6%, 10%, 1);
--accent: hsla(240, 5%, 96%, 1);
--accent-foreground: hsla(240, 6%, 10%, 1);
--accent-90: hsla(240, 5%, 96%, 0.9);
--accent-80: hsla(240, 5%, 96%, 0.8);
--accent-50: hsla(240, 5%, 96%, 0.5);
--accent-90: hsla(240, 5%, 96%, 0.9);
--accent-80: hsla(240, 5%, 96%, 0.8);
--accent-50: hsla(240, 5%, 96%, 0.5);
--destructive: hsla(0, 84%, 60%, 1);
--destructive-foreground: hsla(0, 0%, 98%, 1);
--success: hsla(142, 76%, 36%, 1);
--destructiv-90: hsla(0, 84%, 60%, 0.9);
--destructiv-80: hsla(0, 84%, 60%, 0.8);
--destructiv-50: hsla(0, 84%, 60%, 0.5);
--destructive: hsla(0, 84%, 60%, 1);
--destructive-foreground: hsla(0, 0%, 98%, 1);
--destructiv-90: hsla(0, 84%, 60%, 0.9);
--destructiv-80: hsla(0, 84%, 60%, 0.8);
--destructiv-50: hsla(0, 84%, 60%, 0.5);
--border: hsla(240, 6%, 90%, 1);
--input: hsla(240, 6%, 90%, 1);
--ring: var(--color-neutral-800);
--border: hsla(240, 6%, 90%, 1);
--input: hsla(240, 6%, 90%, 1);
--ring: var(--color-neutral-800);
--radius: calc(1rem);
--spacing: 0.25rem;
--radius: calc(1rem);
--spacing: 0.25rem;
--chart-1: var(--color-orange-400);
--chart-2: var(--color-teal-600);
--chart-3: var(--color-green-800);
--chart-4: var(--color-yellow-200);
--chart-5: var(--color-orange-200);
--chart-1: var(--color-orange-400);
--chart-2: var(--color-teal-600);
--chart-3: var(--color-green-800);
--chart-4: var(--color-yellow-200);
--chart-5: var(--color-orange-200);
--sidebar-background: var(--background);
--sidebar-foreground: var(--foreground);
--sidebar-primary: var(--primary);
--sidebar-primary-foreground: var(--color-white);
--sidebar-accent: var(--secondary);
--sidebar-accent-foreground: var(--secondary-foreground);
--sidebar-border: var(--border);
--sidebar-ring: var(--ring);
}
--sidebar-background: var(--background);
--sidebar-foreground: var(--foreground);
--sidebar-primary: var(--primary);
--sidebar-primary-foreground: var(--color-white);
--sidebar-accent: var(--secondary);
--sidebar-accent-foreground: var(--secondary-foreground);
--sidebar-border: var(--border);
--sidebar-ring: var(--ring);
}
.dark {
--background: var(--color-neutral-900);
--foreground: var(--color-white);
.dark {
--background: var(--color-neutral-900);
--foreground: var(--color-white);
--card: var(--color-neutral-900);
--card-foreground: var(--color-white);
--card: var(--color-neutral-900);
--card-foreground: var(--color-white);
--popover: var(--color-neutral-900);
--popover-foreground: var(--color-white);
--popover: var(--color-neutral-900);
--popover-foreground: var(--color-white);
--primary: var(--color-white);
--primary-foreground: var(--color-neutral-900);
--primary: var(--color-white);
--primary-foreground: var(--color-neutral-900);
--secondary: var(--color-neutral-800);
--secondary-foreground: oklch(98.43% 0.0017 247.84);
--secondary: var(--color-neutral-800);
--secondary-foreground: oklch(98.43% 0.0017 247.84);
--muted: var(--color-neutral-800);
--muted-foreground: hsla(240, 4%, 41%, 1);
--muted: var(--color-neutral-800);
--muted-foreground: hsla(240, 4%, 41%, 1);
--accent: var(--color-neutral-800);
--accent-foreground: oklch(98.48% 0 0);
--accent: var(--color-neutral-800);
--accent-foreground: oklch(98.48% 0 0);
--destructive: var(--color-red-700);
--destructive-foreground: var(--color-white);
--destructive: var(--color-red-700);
--destructive-foreground: var(--color-white);
--border: var(--color-neutral-800);
--input: var(--color-neutral-700);
--ring: oklch(87.09% 0.0055 286.29);
--border: var(--color-neutral-800);
--input: var(--color-neutral-700);
--ring: oklch(87.09% 0.0055 286.29);
--chart-1: var(--color-blue-600);
--chart-2: var(--color-emerald-400);
--chart-3: var(--color-orange-400);
--chart-4: var(--color-purple-500);
--chart-5: var(--color-pink-500);
--chart-1: var(--color-blue-600);
--chart-2: var(--color-emerald-400);
--chart-3: var(--color-orange-400);
--chart-4: var(--color-purple-500);
--chart-5: var(--color-pink-500);
--sidebar-background: var(--color-neutral-900);
--sidebar-foreground: var(--color-white);
--sidebar-primary: var(--color-blue-500);
--sidebar-primary-foreground: var(--color-white);
--sidebar-accent: var(--color-neutral-800);
--sidebar-accent-foreground: var(--color-white);
--sidebar-border: var(--border);
--sidebar-ring: var(--color-blue-500);
}
}
--sidebar-background: var(--color-neutral-900);
--sidebar-foreground: var(--color-white);
--sidebar-primary: var(--color-blue-500);
--sidebar-primary-foreground: var(--color-white);
--sidebar-accent: var(--color-neutral-800);
--sidebar-accent-foreground: var(--color-white);
--sidebar-border: var(--border);
--sidebar-ring: var(--color-blue-500);
}
}

View File

@@ -43,6 +43,15 @@
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--success: hsla(142, 76%, 36%, 1);
--color-success: var(--success);
--warning: hsla(25, 95%, 53%, 1);
--color-warning: var(--warning);
--cyan: hsla(189, 94%, 43%, 1);
--color-cyan: var(--cyan);
/* text colors */
--color-text-foreground: var(--foreground);
--color-text-primary: var(--primary);
@@ -94,7 +103,7 @@
--color-border-primary: var(--primary);
--color-border-primary-50: var(--primary-50);
--color-border-primary-foreground: var(--primary-foreground);
--color-border-destructive: var(--destructive-50);
--color-border-toast-destructive: var(--muted-40);
--color-border-muted: var(--muted);
@@ -123,13 +132,13 @@
--animate-accordion-down: accordion-down 0.2s ease-out;
--animate-accordion-up: accordion-up 0.2s ease-out;
--breakpoint-xs: 30rem;
--breakpoint-xs: 30rem;
--breakpoint-sm: 48rem;
--breakpoint-md: 70rem;
--breakpoint-lg: 80rem;
--breakpoint-xl: 96rem;
--breakpoint-2xl: 100rem;
--breakpoint-3xl: 120rem;
--breakpoint-lg: 80rem;
--breakpoint-xl: 96rem;
--breakpoint-2xl: 100rem;
--breakpoint-3xl: 120rem;
--container-max-width: 80rem;
@@ -180,4 +189,4 @@
transform: translateY(0px);
}
}
}
}