From c48a4b482f233d8e513ac564f63f9efad7d5c3e4 Mon Sep 17 00:00:00 2001 From: Danel Kungla Date: Mon, 18 Aug 2025 14:54:46 +0300 Subject: [PATCH] feat(dashboard): add dynamic loading for dashboard component feat(team-account-benefit-statistics): implement benefit statistics card with budget and booking details feat(team-account-health-details): create health details component displaying account health metrics feat(team-account-statistics): develop team account statistics page with charts and customer table feat(load-team-account-health-details): add server-side function to retrieve account health details chore(migrations): create trigger for logging changes in account memberships --- .../{dashboard-demo.tsx => dashboard.tsx} | 2 +- .../team-account-benefit-statistics.tsx | 128 ++++++++++++ .../team-account-health-details.tsx | 34 ++++ .../team-account-layout-page-header.tsx | 9 +- .../team-account-layout-sidebar.tsx | 11 +- ...charts.tsx => team-account-statistics.tsx} | 184 ++++++++---------- .../load-team-account-health-details.ts | 69 +++++++ app/home/[account]/page.tsx | 23 ++- .../personal-account-dropdown-container.tsx | 4 +- config/team-account-navigation.config.tsx | 2 +- packages/ui/src/shadcn/card.tsx | 1 + public/locales/en/account.json | 2 +- public/locales/en/teams.json | 2 +- public/locales/et/common.json | 4 +- public/locales/et/teams.json | 10 +- .../20250818111200_team_account.sql | 2 + 16 files changed, 362 insertions(+), 125 deletions(-) rename app/home/[account]/_components/{dashboard-demo.tsx => dashboard.tsx} (77%) create mode 100644 app/home/[account]/_components/team-account-benefit-statistics.tsx create mode 100644 app/home/[account]/_components/team-account-health-details.tsx rename app/home/[account]/_components/{dashboard-demo-charts.tsx => team-account-statistics.tsx} (88%) create mode 100644 app/home/[account]/_lib/server/load-team-account-health-details.ts create mode 100644 supabase/migrations/20250818111200_team_account.sql diff --git a/app/home/[account]/_components/dashboard-demo.tsx b/app/home/[account]/_components/dashboard.tsx similarity index 77% rename from app/home/[account]/_components/dashboard-demo.tsx rename to app/home/[account]/_components/dashboard.tsx index 63528f4..67208ba 100644 --- a/app/home/[account]/_components/dashboard-demo.tsx +++ b/app/home/[account]/_components/dashboard.tsx @@ -4,7 +4,7 @@ import dynamic from 'next/dynamic'; import { LoadingOverlay } from '@kit/ui/loading-overlay'; -export const DashboardDemo = dynamic(() => import('./dashboard-demo-charts'), { +export const Dashboard = dynamic(() => import('./team-account-statistics'), { ssr: false, loading: () => ( { + return {children}; +}; + +const StatisticsCardTitle = ({ children }: { children: React.ReactNode }) => { + return {children}; +}; + +const StatisticsDescription = ({ children }: { children: React.ReactNode }) => { + return

{children}

; +}; + +const StatisticsValue = ({ children }: { children: React.ReactNode }) => { + return

{children}

; +}; + +const TeamAccountBenefitStatistics = ({ + accountSlug, +}: TeamAccountBenefitStatisticsProps) => { + const { + i18n: { language }, + } = useTranslation(); + + return ( +
+ +
+ +
+ +
+

+ +

+

+ +

+ + + +
+
+ +
+ + Analüüsid + 18 % + 36 broneeringut + + + Eriarstid ja spetsialistid + 22 % + 44 broneeringut + + + Uuringud + 20 % + 40 broneeringut + + + E-konsultatsioon + 17 % + 34 broneeringut + + + Terviseuuringute paketid +
+
+ 23 % + 46 teenuse kasutust +
+
+ 1800 € + Teenuste summa +
+
+
+
+
+ ); +}; + +export default TeamAccountBenefitStatistics; diff --git a/app/home/[account]/_components/team-account-health-details.tsx b/app/home/[account]/_components/team-account-health-details.tsx new file mode 100644 index 0000000..9e599cf --- /dev/null +++ b/app/home/[account]/_components/team-account-health-details.tsx @@ -0,0 +1,34 @@ +import React from 'react'; + +import { Card } from '@kit/ui/card'; +import { cn } from '@kit/ui/utils'; + +import { + NormStatus, + getAccountHealthDetailsFields, +} from '../_lib/server/load-team-account-health-details'; + +const TeamAccountHealthDetails = () => { + const accountHealthDetailsFields = getAccountHealthDetailsFields(); + return ( +
+ {accountHealthDetailsFields.map(({ title, Icon, value, normStatus }) => ( + +
+ +
+
{title}
+ {value} +
+ ))} +
+ ); +}; + +export default TeamAccountHealthDetails; diff --git a/app/home/[account]/_components/team-account-layout-page-header.tsx b/app/home/[account]/_components/team-account-layout-page-header.tsx index cecd62b..7aac363 100644 --- a/app/home/[account]/_components/team-account-layout-page-header.tsx +++ b/app/home/[account]/_components/team-account-layout-page-header.tsx @@ -2,12 +2,13 @@ import { PageHeader } from '@kit/ui/page'; export function TeamAccountLayoutPageHeader( props: React.PropsWithChildren<{ - title: string | React.ReactNode; - description: string | React.ReactNode; - account: string; + title?: string | React.ReactNode; + description?: string | React.ReactNode; }>, ) { return ( - {props.children} + + {props.children} + ); } diff --git a/app/home/[account]/_components/team-account-layout-sidebar.tsx b/app/home/[account]/_components/team-account-layout-sidebar.tsx index 8b3c385..8f53ef4 100644 --- a/app/home/[account]/_components/team-account-layout-sidebar.tsx +++ b/app/home/[account]/_components/team-account-layout-sidebar.tsx @@ -50,15 +50,15 @@ function SidebarContainer(props: { return ( - -
+ +
-
+
- + diff --git a/app/home/[account]/_components/dashboard-demo-charts.tsx b/app/home/[account]/_components/team-account-statistics.tsx similarity index 88% rename from app/home/[account]/_components/dashboard-demo-charts.tsx rename to app/home/[account]/_components/team-account-statistics.tsx index 436853b..4dac5ae 100644 --- a/app/home/[account]/_components/dashboard-demo-charts.tsx +++ b/app/home/[account]/_components/team-account-statistics.tsx @@ -1,8 +1,19 @@ 'use client'; -import { useMemo, useState } from 'react'; +import { use, useMemo, useState } from 'react'; -import { ArrowDown, ArrowUp, Menu, TrendingUp } from 'lucide-react'; +import { redirect } from 'next/navigation'; + +import { Database } from '@/packages/supabase/src/database.types'; +import { + ArrowDown, + ArrowUp, + ChevronRight, + Euro, + Menu, + TrendingUp, + User, +} from 'lucide-react'; import { Area, AreaChart, @@ -38,7 +49,20 @@ import { TableRow, } from '@kit/ui/table'; -export default function DashboardDemo() { +import pathsConfig from '~/config/paths.config'; +import { createPath } from '~/config/team-account-navigation.config'; + +import { loadCurrentUserAccount } from '../../(user)/_lib/server/load-user-account'; +import TeamAccountBenefitStatistics from './team-account-benefit-statistics'; +import TeamAccountHealthDetails from './team-account-health-details'; + +interface TeamAccountStatisticsProps { + teamAccount: Database['medreport']['Tables']['accounts']['Row']; +} + +export default function TeamAccountStatistics({ + teamAccount, +}: TeamAccountStatisticsProps) { const mrr = useMemo(() => generateDemoData(), []); const netRevenue = useMemo(() => generateDemoData(), []); const fees = useMemo(() => generateDemoData(), []); @@ -50,111 +74,67 @@ export default function DashboardDemo() { 'animate-in fade-in flex flex-col space-y-4 pb-36 duration-500' } > -
- - - - MRR - 20% - + - - Monthly recurring revenue - +
Ettevõtte terviseandmed
+
+ -
-
{`$${mrr[1]}`}
+
+ + redirect( + createPath( + pathsConfig.app.accountMembers, + teamAccount.slug || '', + ), + ) + } + > +
+
+ +
+
+ +
+ + Halda töötajaid + +

+ Lisa, muuda või eemalda töötajaid. +

- +
- - - - - - - - - Revenue - 12% - - - - Total revenue including fees - - -
-
{`$${netRevenue[1]}`}
+ + redirect( + createPath( + pathsConfig.app.accountBilling, + teamAccount.slug || '', + ), + ) + } + > +
+
- - - - - -
- - - - - Fees - 9% - - - - Total fees collected - - -
-
{`$${fees[1]}`}
+
+
- - - - - - - - - - - New Customers - -25% - - - - Customers who signed up this month - - -
-
{`${Number(newCustomers[1]).toFixed(0)}`}
-
-
- - - - -
-
- - - - - -
- - - Best Customers - Showing the top customers by MRR - - - - - - + + Halda eelarvet + +

+ Vali kuidas soovid eelarvet töötajate vahel jagada. +

+ +
); diff --git a/app/home/[account]/_lib/server/load-team-account-health-details.ts b/app/home/[account]/_lib/server/load-team-account-health-details.ts new file mode 100644 index 0000000..18fb787 --- /dev/null +++ b/app/home/[account]/_lib/server/load-team-account-health-details.ts @@ -0,0 +1,69 @@ +import React from 'react'; + +import { Clock, TrendingUp, User } from 'lucide-react'; + +interface AccountHealthDetailsField { + title: string; + value: string; + Icon: React.ComponentType<{ + size?: number; + color?: string; + className?: string; + }>; + normStatus: NormStatus; +} + +export enum NormStatus { + CRITICAL = 'CRITICAL', + WARNING = 'WARNING', + NORMAL = 'NORMAL', +} + +export const getAccountHealthDetailsFields = + (): AccountHealthDetailsField[] => { + const test = ''; + return [ + { + title: 'Naised', + value: '50% (16)', + Icon: User, + normStatus: NormStatus.NORMAL, + }, + { + title: 'Mehed', + value: '50% (16)', + Icon: User, + normStatus: NormStatus.NORMAL, + }, + { + title: 'Keskmine vanus', + value: '56', + Icon: Clock, + normStatus: NormStatus.NORMAL, + }, + { + title: 'KMI', + value: '271', + Icon: TrendingUp, + normStatus: NormStatus.WARNING, + }, + { + title: 'Üldkolesterool', + value: '6.1', + Icon: TrendingUp, + normStatus: NormStatus.WARNING, + }, + { + title: 'Vitamiin D', + value: '76', + Icon: TrendingUp, + normStatus: NormStatus.NORMAL, + }, + { + title: 'Suitsetajad', + value: '22%', + Icon: TrendingUp, + normStatus: NormStatus.CRITICAL, + }, + ]; + }; diff --git a/app/home/[account]/page.tsx b/app/home/[account]/page.tsx index ad389bf..e76b6f7 100644 --- a/app/home/[account]/page.tsx +++ b/app/home/[account]/page.tsx @@ -1,15 +1,18 @@ +'use server'; + import { use } from 'react'; import { CompanyGuard } from '@/packages/features/team-accounts/src/components'; +import { createTeamAccountsApi } from '@/packages/features/team-accounts/src/server/api'; +import { getSupabaseServerClient } from '@/packages/supabase/src/clients/server-client'; -import { AppBreadcrumbs } from '@kit/ui/app-breadcrumbs'; import { PageBody } from '@kit/ui/page'; import { Trans } from '@kit/ui/trans'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; -import { DashboardDemo } from './_components/dashboard-demo'; +import { Dashboard } from './_components/dashboard'; import { TeamAccountLayoutPageHeader } from './_components/team-account-layout-page-header'; interface TeamAccountHomePageProps { @@ -27,17 +30,23 @@ export const generateMetadata = async () => { function TeamAccountHomePage({ params }: TeamAccountHomePageProps) { const account = use(params).account; - console.log('TeamAccountHomePage account', account); + const client = getSupabaseServerClient(); + const api = createTeamAccountsApi(client); + const teamAccount = use(api.getTeamAccount(account)); + console.log('teamAccount', teamAccount); return ( <> } - description={} + title={ + + } /> - + ); diff --git a/components/personal-account-dropdown-container.tsx b/components/personal-account-dropdown-container.tsx index 52e8e5c..ce0d76b 100644 --- a/components/personal-account-dropdown-container.tsx +++ b/components/personal-account-dropdown-container.tsx @@ -13,7 +13,7 @@ const paths = { home: pathsConfig.app.home, admin: pathsConfig.app.admin, doctor: pathsConfig.app.doctor, - personalAccountSettings: pathsConfig.app.personalAccountSettings + personalAccountSettings: pathsConfig.app.personalAccountSettings, }; const features = { @@ -28,11 +28,13 @@ export function ProfileAccountDropdownContainer(props: { id: string | null; name: string | null; picture_url: string | null; + application_role: string; }; accounts: { label: string | null; value: string | null; image?: string | null; + application_role: string; }[]; }) { const signOut = useSignOut(); diff --git a/config/team-account-navigation.config.tsx b/config/team-account-navigation.config.tsx index 1f5bdba..b3a7c6b 100644 --- a/config/team-account-navigation.config.tsx +++ b/config/team-account-navigation.config.tsx @@ -53,6 +53,6 @@ export function getTeamAccountSidebarConfig(account: string) { }); } -function createPath(path: string, account: string) { +export function createPath(path: string, account: string) { return path.replace('[account]', account); } diff --git a/packages/ui/src/shadcn/card.tsx b/packages/ui/src/shadcn/card.tsx index 25a7eef..fec5a6b 100644 --- a/packages/ui/src/shadcn/card.tsx +++ b/packages/ui/src/shadcn/card.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { VariantProps, cva } from 'class-variance-authority'; + import { cn } from '.'; const cardVariants = cva('text-card-foreground rounded-xl border', { diff --git a/public/locales/en/account.json b/public/locales/en/account.json index 43ecce4..2939d6f 100644 --- a/public/locales/en/account.json +++ b/public/locales/en/account.json @@ -126,4 +126,4 @@ "updateRoleSuccess": "Role updated", "updateRoleError": "Something went wrong, please try again", "updateRoleLoading": "Updating role..." -} \ No newline at end of file +} diff --git a/public/locales/en/teams.json b/public/locales/en/teams.json index 6a683e2..2c26931 100644 --- a/public/locales/en/teams.json +++ b/public/locales/en/teams.json @@ -1,6 +1,6 @@ { "home": { - "pageTitle": "Home" + "pageTitle": "Dashboard" }, "settings": { "pageTitle": "Settings", diff --git a/public/locales/et/common.json b/public/locales/et/common.json index a259c6a..4c2473a 100644 --- a/public/locales/et/common.json +++ b/public/locales/et/common.json @@ -77,7 +77,7 @@ "account": "Account", "members": "Members", "billing": "Billing", - "dashboard": "Dashboard", + "dashboard": "Ülevaade", "settings": "Settings", "profile": "Profile", "application": "Application" @@ -129,4 +129,4 @@ "expiredAt": "Kehtiv kuni {{expiredAt}}" }, "doctor": "Arst" -} \ No newline at end of file +} diff --git a/public/locales/et/teams.json b/public/locales/et/teams.json index 3953e7f..66a8f74 100644 --- a/public/locales/et/teams.json +++ b/public/locales/et/teams.json @@ -1,6 +1,7 @@ { "home": { - "pageTitle": "Home" + "pageTitle": "Ülevaade", + "headerTitle": "{{companyName}} tervise ülevaade" }, "settings": { "pageTitle": "Settings", @@ -18,6 +19,13 @@ "billing": { "pageTitle": "Billing" }, + "benefitStatistics": { + "budget": { + "title": "Ettevõtte Tervisekassa seis", + "balance": "Eelarve jääk {{balance}}", + "volume": "Eelarve maht {{volume}}" + } + }, "yourTeams": "Your Companies ({{teamsCount}})", "createTeam": "Create a Company", "creatingTeam": "Creating Company...", diff --git a/supabase/migrations/20250818111200_team_account.sql b/supabase/migrations/20250818111200_team_account.sql new file mode 100644 index 0000000..56ab1ae --- /dev/null +++ b/supabase/migrations/20250818111200_team_account.sql @@ -0,0 +1,2 @@ +CREATE TRIGGER log_account_change AFTER DELETE OR UPDATE ON medreport.accounts_memberships FOR EACH ROW EXECUTE FUNCTION audit.log_audit_changes(); +