feat(team-accounts): enhance team account statistics and health details components with new data and improved calculations

This commit is contained in:
Danel Kungla
2025-08-25 12:24:27 +03:00
parent ee86bb8829
commit 56ffd7591e
12 changed files with 336 additions and 150 deletions

View File

@@ -3,10 +3,12 @@ import React, { use } from 'react';
import { redirect } from 'next/navigation'; import { redirect } from 'next/navigation';
import { formatCurrency } from '@/packages/shared/src/utils'; import { formatCurrency } from '@/packages/shared/src/utils';
import { Database } from '@/packages/supabase/src/database.types';
import { PiggyBankIcon, Settings } from 'lucide-react'; import { PiggyBankIcon, Settings } from 'lucide-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Card, CardTitle } from '@kit/ui/card'; import { Card, CardTitle } from '@kit/ui/card';
import { cn } from '@kit/ui/lib/utils';
import { Button } from '@kit/ui/shadcn/button'; import { Button } from '@kit/ui/shadcn/button';
import { Trans } from '@kit/ui/trans'; import { Trans } from '@kit/ui/trans';
@@ -14,15 +16,27 @@ import pathsConfig from '~/config/paths.config';
import { createPath } from '~/config/team-account-navigation.config'; import { createPath } from '~/config/team-account-navigation.config';
interface TeamAccountBenefitStatisticsProps { interface TeamAccountBenefitStatisticsProps {
employeeCount: number;
accountSlug: string; accountSlug: string;
companyParams: Database['medreport']['Tables']['company_params']['Row'];
} }
const StatisticsCard = ({ children }: { children: React.ReactNode }) => { const StatisticsCard = ({ children }: { children: React.ReactNode }) => {
return <Card className="p-4">{children}</Card>; return <Card className="p-4">{children}</Card>;
}; };
const StatisticsCardTitle = ({ children }: { children: React.ReactNode }) => { const StatisticsCardTitle = ({
return <CardTitle className="text-sm font-medium">{children}</CardTitle>; children,
className,
}: {
children: React.ReactNode;
className?: string;
}) => {
return (
<CardTitle className={cn('text-sm font-medium', className)}>
{children}
</CardTitle>
);
}; };
const StatisticsDescription = ({ children }: { children: React.ReactNode }) => { const StatisticsDescription = ({ children }: { children: React.ReactNode }) => {
@@ -34,7 +48,9 @@ const StatisticsValue = ({ children }: { children: React.ReactNode }) => {
}; };
const TeamAccountBenefitStatistics = ({ const TeamAccountBenefitStatistics = ({
employeeCount,
accountSlug, accountSlug,
companyParams,
}: TeamAccountBenefitStatisticsProps) => { }: TeamAccountBenefitStatisticsProps) => {
const { const {
i18n: { language }, i18n: { language },
@@ -76,7 +92,8 @@ const TeamAccountBenefitStatistics = ({
i18nKey="teams:benefitStatistics.budget.volume" i18nKey="teams:benefitStatistics.budget.volume"
values={{ values={{
volume: formatCurrency({ volume: formatCurrency({
value: 15000, value:
(Number(companyParams.benefit_amount) || 0) * employeeCount,
locale: language, locale: language,
currencyCode: 'EUR', currencyCode: 'EUR',
}), }),
@@ -87,11 +104,17 @@ const TeamAccountBenefitStatistics = ({
</Card> </Card>
<div className="grid flex-2 grid-cols-2 gap-2 sm:grid-cols-3 sm:grid-rows-2"> <div className="grid flex-2 grid-cols-2 gap-2 sm:grid-cols-3 sm:grid-rows-2">
<StatisticsCard>
<StatisticsCardTitle className="text-lg font-bold">
<Trans i18nKey="teams:benefitStatistics.data.serviceSum" />
</StatisticsCardTitle>
<StatisticsValue>1800 </StatisticsValue>
</StatisticsCard>
<StatisticsCard> <StatisticsCard>
<StatisticsCardTitle> <StatisticsCardTitle>
<Trans i18nKey="teams:benefitStatistics.data.analysis" /> <Trans i18nKey="teams:benefitStatistics.data.analysis" />
</StatisticsCardTitle> </StatisticsCardTitle>
<StatisticsValue>18 %</StatisticsValue> <StatisticsValue>200 </StatisticsValue>
<StatisticsDescription> <StatisticsDescription>
<Trans <Trans
i18nKey="teams:benefitStatistics.data.reservations" i18nKey="teams:benefitStatistics.data.reservations"
@@ -103,7 +126,7 @@ const TeamAccountBenefitStatistics = ({
<StatisticsCardTitle> <StatisticsCardTitle>
<Trans i18nKey="teams:benefitStatistics.data.doctorsAndSpecialists" /> <Trans i18nKey="teams:benefitStatistics.data.doctorsAndSpecialists" />
</StatisticsCardTitle> </StatisticsCardTitle>
<StatisticsValue>22 %</StatisticsValue> <StatisticsValue>200 </StatisticsValue>
<StatisticsDescription> <StatisticsDescription>
<Trans <Trans
i18nKey="teams:benefitStatistics.data.reservations" i18nKey="teams:benefitStatistics.data.reservations"
@@ -115,7 +138,7 @@ const TeamAccountBenefitStatistics = ({
<StatisticsCardTitle> <StatisticsCardTitle>
<Trans i18nKey="teams:benefitStatistics.data.researches" /> <Trans i18nKey="teams:benefitStatistics.data.researches" />
</StatisticsCardTitle> </StatisticsCardTitle>
<StatisticsValue>20 %</StatisticsValue> <StatisticsValue>200 </StatisticsValue>
<StatisticsDescription> <StatisticsDescription>
<Trans <Trans
i18nKey="teams:benefitStatistics.data.reservations" i18nKey="teams:benefitStatistics.data.reservations"
@@ -124,8 +147,10 @@ const TeamAccountBenefitStatistics = ({
</StatisticsDescription> </StatisticsDescription>
</StatisticsCard> </StatisticsCard>
<StatisticsCard> <StatisticsCard>
<StatisticsCardTitle>E-konsultatsioon</StatisticsCardTitle> <StatisticsCardTitle>
<StatisticsValue>17 %</StatisticsValue> <Trans i18nKey="teams:benefitStatistics.data.eclinic" />
</StatisticsCardTitle>
<StatisticsValue>200 </StatisticsValue>
<StatisticsDescription> <StatisticsDescription>
<Trans <Trans
i18nKey="teams:benefitStatistics.data.reservations" i18nKey="teams:benefitStatistics.data.reservations"
@@ -133,28 +158,19 @@ const TeamAccountBenefitStatistics = ({
/> />
</StatisticsDescription> </StatisticsDescription>
</StatisticsCard> </StatisticsCard>
<Card className="col-span-2 p-4">
<StatisticsCard>
<StatisticsCardTitle> <StatisticsCardTitle>
<Trans i18nKey="teams:benefitStatistics.data.healthResearchPlans" /> <Trans i18nKey="teams:benefitStatistics.data.healthResearchPlans" />
</StatisticsCardTitle> </StatisticsCardTitle>
<div className="grid grid-cols-2 gap-2"> <StatisticsValue>200 </StatisticsValue>
<div className="border-r"> <StatisticsDescription>
<StatisticsValue>23 %</StatisticsValue> <Trans
<StatisticsDescription> i18nKey="teams:benefitStatistics.data.serviceUsage"
<Trans values={{ value: 46 }}
i18nKey="teams:benefitStatistics.data.serviceUsage" />
values={{ value: 46 }} </StatisticsDescription>
/> </StatisticsCard>
</StatisticsDescription>
</div>
<div className="ml-4">
<StatisticsValue>1800 </StatisticsValue>
<StatisticsDescription>
<Trans i18nKey="teams:benefitStatistics.data.serviceSum" />
</StatisticsDescription>
</div>
</div>
</Card>
</div> </div>
</div> </div>
); );

View File

@@ -1,33 +1,36 @@
import React from 'react'; import React from 'react';
import { Database } from '@/packages/supabase/src/database.types';
import { Card } from '@kit/ui/card'; import { Card } from '@kit/ui/card';
import { Trans } from '@kit/ui/trans'; import { Trans } from '@kit/ui/trans';
import { cn } from '@kit/ui/utils'; import { cn } from '@kit/ui/utils';
import { import { getAccountHealthDetailsFields } from '../_lib/server/load-team-account-health-details';
NormStatus,
getAccountHealthDetailsFields,
} from '../_lib/server/load-team-account-health-details';
import { TeamAccountStatisticsProps } from './team-account-statistics'; import { TeamAccountStatisticsProps } from './team-account-statistics';
const TeamAccountHealthDetails = ({ const TeamAccountHealthDetails = ({
memberParams, memberParams,
bmiThresholds,
members,
}: { }: {
memberParams: TeamAccountStatisticsProps['memberParams']; memberParams: TeamAccountStatisticsProps['memberParams'];
bmiThresholds: Omit<
Database['medreport']['Tables']['bmi_thresholds']['Row'],
'id'
>[];
members: Database['medreport']['Functions']['get_account_members']['Returns'];
}) => { }) => {
const accountHealthDetailsFields = const accountHealthDetailsFields = getAccountHealthDetailsFields(
getAccountHealthDetailsFields(memberParams); memberParams,
bmiThresholds,
members,
);
return ( return (
<div className="grid flex-1 grid-cols-2 gap-4 md:grid-cols-3"> <div className="grid flex-1 grid-cols-2 gap-4 md:grid-cols-3">
{accountHealthDetailsFields.map(({ title, Icon, value, normStatus }) => ( {accountHealthDetailsFields.map(({ title, Icon, value, iconBg }) => (
<Card className="relative p-4" key={title}> <Card className="relative p-4" key={title}>
<div <div className={cn('absolute top-2 right-2 rounded-2xl p-2', iconBg)}>
className={cn('absolute top-2 right-2 rounded-2xl p-2', {
'bg-success': normStatus === NormStatus.NORMAL,
'bg-warning': normStatus === NormStatus.WARNING,
'bg-destructive': normStatus === NormStatus.CRITICAL,
})}
>
<Icon color="white" className="stroke-2" /> <Icon color="white" className="stroke-2" />
</div> </div>
<h5 className="mt-8 leading-none"> <h5 className="mt-8 leading-none">

View File

@@ -1,12 +1,24 @@
'use client'; 'use client';
import { useState } from 'react';
import { redirect } from 'next/navigation'; import { redirect } from 'next/navigation';
import { Database } from '@/packages/supabase/src/database.types'; import { Database } from '@/packages/supabase/src/database.types';
import { ChevronRight, Euro, User } from 'lucide-react'; import { format } from 'date-fns';
import { enGB, et } from 'date-fns/locale';
import { CalendarIcon, ChevronRight, Euro, User } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { Card } from '@kit/ui/card'; import { Card } from '@kit/ui/card';
import { Trans } from '@kit/ui/makerkit/trans'; import { Trans } from '@kit/ui/makerkit/trans';
import { Button } from '@kit/ui/shadcn/button';
import { Calendar, DateRange } from '@kit/ui/shadcn/calendar';
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@kit/ui/shadcn/popover';
import pathsConfig from '~/config/paths.config'; import pathsConfig from '~/config/paths.config';
import { createPath } from '~/config/team-account-navigation.config'; import { createPath } from '~/config/team-account-navigation.config';
@@ -20,82 +32,145 @@ export interface TeamAccountStatisticsProps {
Database['medreport']['Tables']['account_params']['Row'], Database['medreport']['Tables']['account_params']['Row'],
'weight' | 'height' 'weight' | 'height'
>[]; >[];
bmiThresholds: Omit<
Database['medreport']['Tables']['bmi_thresholds']['Row'],
'id'
>[];
members: Database['medreport']['Functions']['get_account_members']['Returns'];
companyParams: Database['medreport']['Tables']['company_params']['Row'];
} }
export default function TeamAccountStatistics({ export default function TeamAccountStatistics({
teamAccount, teamAccount,
memberParams, memberParams,
bmiThresholds,
members,
companyParams,
}: TeamAccountStatisticsProps) { }: TeamAccountStatisticsProps) {
const [date, setDate] = useState<DateRange | undefined>({
from: new Date(),
to: new Date(),
});
const {
i18n: { language },
} = useTranslation();
const dateFormatOptions = {
locale: language === 'et' ? et : enGB,
};
return ( return (
<div <>
className={ <div className="mt-4 flex items-center justify-between">
'animate-in fade-in flex flex-col space-y-4 pb-36 duration-500' <h4 className="font-bold">
} <Trans
> i18nKey={'teams:home.headerTitle'}
<TeamAccountBenefitStatistics accountSlug={teamAccount.slug || ''} /> values={{ companyName: teamAccount.name }}
/>
</h4>
<h5 className="mt-4 mb-2"> <Popover>
<Trans i18nKey="teams:home.healthDetails" /> <PopoverTrigger asChild>
</h5> <Button variant="outline" data-empty={!date}>
<div className="flex flex-col gap-2 sm:flex-row"> <CalendarIcon />
<TeamAccountHealthDetails memberParams={memberParams} /> {date?.from && date?.to ? (
`${format(date.from, 'd MMMM yyyy', dateFormatOptions)} - ${format(date.to, 'd MMMM yyyy', dateFormatOptions)}`
) : (
<span>
<Trans i18nKey="common:formField.date" />
</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="range"
selected={date}
onSelect={setDate}
locale={language === 'et' ? et : enGB}
/>
</PopoverContent>
</Popover>
</div>
<div className="flex flex-col gap-2"> <div
<Card className={
variant="gradient-success" 'animate-in fade-in flex flex-col space-y-4 pb-36 duration-500'
className="border-success/50 hover:bg-success/20 relative flex h-full cursor-pointer flex-col justify-center px-6 py-4 transition-colors" }
onClick={() => >
redirect( <TeamAccountBenefitStatistics
createPath( employeeCount={members.length}
pathsConfig.app.accountMembers, accountSlug={teamAccount.slug || ''}
teamAccount.slug || '', companyParams={companyParams}
), />
)
} <h5 className="mt-4 mb-2">
> <Trans i18nKey="teams:home.healthDetails" />
<div className="flex flex-col"> </h5>
<div className="flex flex-col gap-2 sm:flex-row">
<TeamAccountHealthDetails
memberParams={memberParams}
bmiThresholds={bmiThresholds}
members={members}
/>
<div className="flex flex-col gap-2">
<Card
variant="gradient-success"
className="border-success/50 hover:bg-success/20 relative flex h-full cursor-pointer flex-col justify-center px-6 py-4 transition-colors"
onClick={() =>
redirect(
createPath(
pathsConfig.app.accountMembers,
teamAccount.slug || '',
),
)
}
>
<div className="flex flex-col">
<div className="border-input absolute top-2 right-2 rounded-md border bg-white p-3">
<ChevronRight className="stroke-2" />
</div>
<div className="bg-primary/10 w-fit rounded-2xl p-2">
<User color="green" className="stroke-2" />
</div>
<span className="mt-4 mb-2 text-lg font-semibold">
<Trans i18nKey="teams:home.membersSettingsButtonTitle" />
</span>
<p className="text-muted-foreground text-sm">
<Trans i18nKey="teams:home.membersSettingsButtonDescription" />
</p>
</div>
</Card>
<Card
variant="gradient-warning"
className="border-warning/40 hover:bg-warning/20 relative flex h-full cursor-pointer flex-col justify-center px-6 py-4 transition-colors"
onClick={() =>
redirect(
createPath(
pathsConfig.app.accountBilling,
teamAccount.slug || '',
),
)
}
>
<div className="border-input absolute top-2 right-2 rounded-md border bg-white p-3"> <div className="border-input absolute top-2 right-2 rounded-md border bg-white p-3">
<ChevronRight className="stroke-2" /> <ChevronRight className="stroke-2" />
</div> </div>
<div className="bg-primary/10 w-fit rounded-2xl p-2"> <div className="bg-warning/10 w-fit rounded-2xl p-2">
<User color="green" className="stroke-2" /> <Euro color="orange" className="stroke-2" />
</div> </div>
<span className="mt-4 mb-2 text-lg font-semibold"> <span className="mt-4 mb-2 text-lg font-semibold">
<Trans i18nKey="teams:home.membersSettingsButtonTitle" /> <Trans i18nKey="teams:home.membersBillingButtonTitle" />
</span> </span>
<p className="text-muted-foreground text-sm"> <p className="text-muted-foreground text-sm">
<Trans i18nKey="teams:home.membersSettingsButtonDescription" /> <Trans i18nKey="teams:home.membersBillingButtonDescription" />
</p> </p>
</div> </Card>
</Card> </div>
<Card
variant="gradient-warning"
className="border-warning/40 hover:bg-warning/20 relative flex h-full cursor-pointer flex-col justify-center px-6 py-4 transition-colors"
onClick={() =>
redirect(
createPath(
pathsConfig.app.accountBilling,
teamAccount.slug || '',
),
)
}
>
<div className="border-input absolute top-2 right-2 rounded-md border bg-white p-3">
<ChevronRight className="stroke-2" />
</div>
<div className="bg-warning/10 w-fit rounded-2xl p-2">
<Euro color="orange" className="stroke-2" />
</div>
<span className="mt-4 mb-2 text-lg font-semibold">
<Trans i18nKey="teams:home.membersBillingButtonTitle" />
</span>
<p className="text-muted-foreground text-sm">
<Trans i18nKey="teams:home.membersBillingButtonDescription" />
</p>
</Card>
</div> </div>
</div> </div>
</div> </>
); );
} }

View File

@@ -1,8 +1,14 @@
import React from 'react'; import React from 'react';
import { Database } from '@/packages/supabase/src/database.types';
import Isikukood from 'isikukood';
import { Clock, TrendingUp, User } from 'lucide-react'; import { Clock, TrendingUp, User } from 'lucide-react';
import { bmiFromMetric } from '~/lib/utils'; import {
bmiFromMetric,
getBmiBackgroundColor,
getBmiStatus,
} from '~/lib/utils';
import { TeamAccountStatisticsProps } from '../../_components/team-account-statistics'; import { TeamAccountStatisticsProps } from '../../_components/team-account-statistics';
@@ -14,66 +20,89 @@ interface AccountHealthDetailsField {
color?: string; color?: string;
className?: string; className?: string;
}>; }>;
normStatus: NormStatus; iconBg: string;
}
export enum NormStatus {
CRITICAL = 'CRITICAL',
WARNING = 'WARNING',
NORMAL = 'NORMAL',
} }
export const getAccountHealthDetailsFields = ( export const getAccountHealthDetailsFields = (
memberParams: TeamAccountStatisticsProps['memberParams'], memberParams: TeamAccountStatisticsProps['memberParams'],
bmiThresholds: Omit<
Database['medreport']['Tables']['bmi_thresholds']['Row'],
'id'
>[],
members: Database['medreport']['Functions']['get_account_members']['Returns'],
): AccountHealthDetailsField[] => { ): AccountHealthDetailsField[] => {
const averageBMI = ( const avarageWeight =
memberParams.reduce((sum, { height, weight }) => { memberParams.reduce((sum, r) => sum + r.weight!, 0) / memberParams.length;
return bmiFromMetric(weight ?? 0, height ?? 0) + sum; const avarageHeight =
}, 0) / memberParams.length memberParams.reduce((sum, r) => sum + r.height!, 0) / memberParams.length;
).toFixed(0); const avarageAge =
members.reduce((sum, r) => {
const person = new Isikukood(r.personal_code);
return sum + person.getAge();
}, 0) / members.length;
const numberOfMaleMembers = members.filter((r) => {
const person = new Isikukood(r.personal_code);
return person.getGender() === 'male';
}).length;
const numberOfFemaleMembers = members.filter((r) => {
const person = new Isikukood(r.personal_code);
return person.getGender() === 'female';
}).length;
const averageBMI = bmiFromMetric(avarageWeight, avarageHeight);
const bmiStatus = getBmiStatus(bmiThresholds, {
age: avarageAge,
height: avarageHeight,
weight: avarageWeight,
});
const malePercentage = members.length
? (numberOfMaleMembers / members.length) * 100
: 0;
const femalePercentage = members.length
? (numberOfFemaleMembers / members.length) * 100
: 0;
return [ return [
{ {
title: 'teams:healthDetails.women', title: 'teams:healthDetails.women',
value: `50% (${memberParams.length})`, value: `${femalePercentage}% (${numberOfFemaleMembers})`,
Icon: User, Icon: User,
normStatus: NormStatus.NORMAL, iconBg: 'bg-success',
}, },
{ {
title: 'teams:healthDetails.men', title: 'teams:healthDetails.men',
value: `50% (${memberParams.length})`, value: `${malePercentage}% (${numberOfMaleMembers})`,
Icon: User, Icon: User,
normStatus: NormStatus.NORMAL, iconBg: 'bg-success',
}, },
{ {
title: 'teams:healthDetails.avgAge', title: 'teams:healthDetails.avgAge',
value: '56', value: avarageAge.toFixed(0),
Icon: Clock, Icon: Clock,
normStatus: NormStatus.NORMAL, iconBg: 'bg-success',
}, },
{ {
title: 'teams:healthDetails.bmi', title: 'teams:healthDetails.bmi',
value: averageBMI, value: averageBMI,
Icon: TrendingUp, Icon: TrendingUp,
normStatus: NormStatus.WARNING, iconBg: getBmiBackgroundColor(bmiStatus),
}, },
{ {
title: 'teams:healthDetails.cholesterol', title: 'teams:healthDetails.cholesterol',
value: '6.1', value: '-',
Icon: TrendingUp, Icon: TrendingUp,
normStatus: NormStatus.WARNING, iconBg: 'bg-warning',
}, },
{ {
title: 'teams:healthDetails.vitaminD', title: 'teams:healthDetails.vitaminD',
value: '76', value: '-',
Icon: TrendingUp, Icon: TrendingUp,
normStatus: NormStatus.NORMAL, iconBg: 'bg-warning',
}, },
{ {
title: 'teams:healthDetails.smokers', title: 'teams:healthDetails.smokers',
value: '22%', value: '-',
Icon: TrendingUp, Icon: TrendingUp,
normStatus: NormStatus.CRITICAL, iconBg: 'bg-warning',
}, },
]; ];
}; };

View File

@@ -2,6 +2,7 @@
import { use } from 'react'; import { use } from 'react';
import { createAccountsApi } from '@/packages/features/accounts/src/server/api';
import { CompanyGuard } from '@/packages/features/team-accounts/src/components'; import { CompanyGuard } from '@/packages/features/team-accounts/src/components';
import { createTeamAccountsApi } from '@/packages/features/team-accounts/src/server/api'; import { createTeamAccountsApi } from '@/packages/features/team-accounts/src/server/api';
import { getSupabaseServerClient } from '@/packages/supabase/src/clients/server-client'; import { getSupabaseServerClient } from '@/packages/supabase/src/clients/server-client';
@@ -11,6 +12,10 @@ import { Trans } from '@kit/ui/trans';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n'; import { withI18n } from '~/lib/i18n/with-i18n';
import {
PAGE_VIEW_ACTION,
createPageViewLog,
} from '~/lib/services/audit/pageView.service';
import { Dashboard } from './_components/dashboard'; import { Dashboard } from './_components/dashboard';
import { TeamAccountLayoutPageHeader } from './_components/team-account-layout-page-header'; import { TeamAccountLayoutPageHeader } from './_components/team-account-layout-page-header';
@@ -31,25 +36,32 @@ export const generateMetadata = async () => {
function TeamAccountHomePage({ params }: TeamAccountHomePageProps) { function TeamAccountHomePage({ params }: TeamAccountHomePageProps) {
const account = use(params).account; const account = use(params).account;
const client = getSupabaseServerClient(); const client = getSupabaseServerClient();
const api = createTeamAccountsApi(client); const teamAccountsApi = createTeamAccountsApi(client);
const teamAccount = use(api.getTeamAccount(account)); const accountsApi = createAccountsApi(client);
const { memberParams } = use(api.getMembers(account)); const teamAccount = use(teamAccountsApi.getTeamAccount(account));
const { memberParams, members } = use(teamAccountsApi.getMembers(account));
const bmiThresholds = use(accountsApi.fetchBmiThresholds());
const companyParams = use(
teamAccountsApi.getTeamAccountParams(teamAccount.id),
);
use(
createPageViewLog({
accountId: teamAccount.id,
action: PAGE_VIEW_ACTION.VIEW_TEAM_ACCOUNT_DASHBOARD,
}),
);
return ( return (
<> <PageBody>
<TeamAccountLayoutPageHeader <Dashboard
title={ teamAccount={teamAccount}
<Trans memberParams={memberParams}
i18nKey={'teams:home.headerTitle'} bmiThresholds={bmiThresholds}
values={{ companyName: account }} members={members}
/> companyParams={companyParams}
}
/> />
</PageBody>
<PageBody>
<Dashboard teamAccount={teamAccount} memberParams={memberParams} />
</PageBody>
</>
); );
} }

View File

@@ -4,6 +4,7 @@ export enum PAGE_VIEW_ACTION {
VIEW_ANALYSIS_RESULTS = 'VIEW_ANALYSIS_RESULTS', VIEW_ANALYSIS_RESULTS = 'VIEW_ANALYSIS_RESULTS',
REGISTRATION_SUCCESS = 'REGISTRATION_SUCCESS', REGISTRATION_SUCCESS = 'REGISTRATION_SUCCESS',
VIEW_ORDER_ANALYSIS = 'VIEW_ORDER_ANALYSIS', VIEW_ORDER_ANALYSIS = 'VIEW_ORDER_ANALYSIS',
VIEW_TEAM_ACCOUNT_DASHBOARD = 'VIEW_TEAM_ACCOUNT_DASHBOARD',
} }
export const createPageViewLog = async ({ export const createPageViewLog = async ({

View File

@@ -70,15 +70,13 @@ export function getBmiStatus(
export function getBmiBackgroundColor(bmiStatus: BmiCategory | null): string { export function getBmiBackgroundColor(bmiStatus: BmiCategory | null): string {
switch (bmiStatus) { switch (bmiStatus) {
case BmiCategory.UNDER_WEIGHT: case BmiCategory.UNDER_WEIGHT:
case BmiCategory.OVER_WEIGHT:
return 'bg-warning'; return 'bg-warning';
case BmiCategory.NORMAL: case BmiCategory.NORMAL:
return 'bg-success'; return 'bg-success';
case BmiCategory.OVER_WEIGHT:
return 'bg-warning';
case BmiCategory.VERY_OVERWEIGHT: case BmiCategory.VERY_OVERWEIGHT:
return 'bg-destructive';
case BmiCategory.OBESE: case BmiCategory.OBESE:
return 'bg-error'; return 'bg-destructive';
default: default:
return 'bg-success'; return 'bg-success';
} }

View File

@@ -291,6 +291,7 @@ export class TeamAccountsApi {
.single(); .single();
if (error) { if (error) {
console.warn('Error fetching company params', error);
throw error; throw error;
} }

View File

@@ -1726,6 +1726,7 @@ export type Database = {
primary_owner_user_id: string primary_owner_user_id: string
name: string name: string
email: string email: string
personal_code: string
picture_url: string picture_url: string
created_at: string created_at: string
updated_at: string updated_at: string

View File

@@ -122,7 +122,8 @@
"weight": "Kaal", "weight": "Kaal",
"height": "Pikkus", "height": "Pikkus",
"occurance": "Toetuse sagedus", "occurance": "Toetuse sagedus",
"amount": "Summa" "amount": "Summa",
"date": "Vali kuupäev"
}, },
"wallet": { "wallet": {
"balance": "Sinu MedReporti konto seis", "balance": "Sinu MedReporti konto seis",

View File

@@ -1,7 +1,7 @@
{ {
"home": { "home": {
"pageTitle": "Ülevaade", "pageTitle": "Ülevaade",
"headerTitle": "{{companyName}} tervise ülevaade", "headerTitle": "{{companyName}} Tervisekassa kokkuvõte",
"healthDetails": "Ettevõtte terviseandmed", "healthDetails": "Ettevõtte terviseandmed",
"membersSettingsButtonTitle": "Halda töötajaid", "membersSettingsButtonTitle": "Halda töötajaid",
"membersSettingsButtonDescription": "Lisa, muuda või eemalda töötajaid.", "membersSettingsButtonDescription": "Lisa, muuda või eemalda töötajaid.",
@@ -31,13 +31,14 @@
"volume": "Eelarve maht {{volume}}" "volume": "Eelarve maht {{volume}}"
}, },
"data": { "data": {
"reservations": "{{value}} broneeringut", "reservations": "{{value}} teenust",
"analysis": "Analüüsid", "analysis": "Analüüsid",
"doctorsAndSpecialists": "Eriarstid ja spetsialistid", "doctorsAndSpecialists": "Eriarstid ja spetsialistid",
"researches": "Uuringud", "researches": "Uuringud",
"healthResearchPlans": "Terviseuuringute paketid", "healthResearchPlans": "Terviseuuringute paketid",
"serviceUsage": "{{value}} teenuse kasutust", "serviceUsage": "{{value}} teenuse kasutust",
"serviceSum": "Teenuste summa" "serviceSum": "Teenuste summa",
"eclinic": "Digikliinik"
} }
}, },
"healthDetails": { "healthDetails": {

View File

@@ -0,0 +1,48 @@
DROP FUNCTION IF EXISTS medreport.get_account_members(text);
CREATE OR REPLACE FUNCTION medreport.get_account_members(account_slug text)
RETURNS TABLE(
id uuid,
user_id uuid,
account_id uuid,
role character varying,
role_hierarchy_level integer,
primary_owner_user_id uuid,
name character varying,
email character varying,
personal_code text,
picture_url character varying,
created_at timestamp with time zone,
updated_at timestamp with time zone
)
LANGUAGE plpgsql
SET search_path TO ''
AS $function$begin
return QUERY
select
acc.id,
am.user_id,
am.account_id,
am.account_role,
r.hierarchy_level,
a.primary_owner_user_id,
acc.name,
acc.email,
acc.personal_code,
acc.picture_url,
am.created_at,
am.updated_at
from
medreport.accounts_memberships am
join medreport.accounts a on a.id = am.account_id
join medreport.accounts acc on acc.id = am.user_id
join medreport.roles r on r.name = am.account_role
where
a.slug = account_slug;
end;$function$
;
grant
execute on function medreport.get_account_members (text) to authenticated,
service_role;