diff --git a/README.md b/README.md index 3ace0ef..f3cca27 100644 --- a/README.md +++ b/README.md @@ -106,10 +106,12 @@ To access admin pages follow these steps: 1. Customer adds analysis to cart in **B2B** storefront 2. Customer checks out from cart and is redirected to **Montonio** 3. Customer pays and is redirected back to **B2B** `GET B2B/home/cart/montonio-callback?order-token=$JWT` - - **Medusa** order is created and cart is emptied - - email is sent to customer - - B2B sends order XML as private message to Medipost. - When **Montonio** has confirmed payment, it will call **Medusa** webhook endpoint and **Medusa** will mark order payment as captured. + +- **Medusa** order is created and cart is emptied +- email is sent to customer +- B2B sends order XML as private message to Medipost. + +When **Montonio** has confirmed payment, it will call **Medusa** webhook endpoint and **Medusa** will mark order payment as captured. In background a job will call `POST B2B/api/job/sync-analysis-results` every n minutes and sync private messages with responses from **Medipost**. @@ -121,7 +123,7 @@ In local dev environment, you can import products from B2B to Medusa with this A - `POST /api/job/sync-analysis-groups-store` - Syncs required data of `analyses`, `analysis_elements` data from **B2B** to **Medusa** and creates relevant products and categories. - If product or category already exists, then it is not recreated. Old entries are not deleted either currently. + If product or category already exists, then it is not recreated. Old entries are not deleted either currently. ## Jobs diff --git a/app/auth/update-account/_components/update-account-form.tsx b/app/auth/update-account/_components/update-account-form.tsx index d286e88..58887f0 100644 --- a/app/auth/update-account/_components/update-account-form.tsx +++ b/app/auth/update-account/_components/update-account-form.tsx @@ -8,8 +8,6 @@ import { ExternalLink } from '@/public/assets/external-link'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; -import { onUpdateAccount } from '@kit/auth/actions/update-account-actions'; -import { UpdateAccountSchema } from '@kit/auth/schemas/update-account.schema'; import { Button } from '@kit/ui/button'; import { Checkbox } from '@kit/ui/checkbox'; import { @@ -23,6 +21,9 @@ import { import { Input } from '@kit/ui/input'; import { Trans } from '@kit/ui/trans'; +import { UpdateAccountSchema } from '../_lib/schemas/update-account.schema'; +import { onUpdateAccount } from '../_lib/server/update-account'; + export function UpdateAccountForm({ user }: { user: User }) { const form = useForm({ resolver: zodResolver(UpdateAccountSchema), @@ -30,16 +31,15 @@ export function UpdateAccountForm({ user }: { user: User }) { defaultValues: { firstName: '', lastName: '', - personalCode: user.user_metadata.personalCode ?? '', + personalCode: '', email: user.email, phone: '', city: '', - weight: user.user_metadata.weight ?? undefined, - height: user.user_metadata.height ?? undefined, + weight: 0, + height: 0, userConsent: false, }, }); - return (
{ diff --git a/app/home/(user)/(dashboard)/analysis-results/_components/analysis-level-bar.tsx b/app/home/(user)/(dashboard)/analysis-results/_components/analysis-level-bar.tsx index 6714f80..c092fe5 100644 --- a/app/home/(user)/(dashboard)/analysis-results/_components/analysis-level-bar.tsx +++ b/app/home/(user)/(dashboard)/analysis-results/_components/analysis-level-bar.tsx @@ -42,7 +42,7 @@ const Level = ({ export const AnalysisLevelBarSkeleton = () => { return ( -
+
); @@ -58,7 +58,7 @@ const AnalysisLevelBar = ({ level: AnalysisResultLevel; }) => { return ( -
+
{normLowerIncluded && ( <> +
{name} {results?.response_time && ( @@ -75,7 +79,9 @@ const Analysis = ({ { block: showTooltip }, )} > - {': '}{format(new Date(results.response_time), 'dd.MM.yyyy HH:mm')} + + {': '} + {format(new Date(results.response_time), 'dd.MM.yyyy HH:mm')}
)} @@ -86,7 +92,7 @@ const Analysis = ({
{value}
{unit}
-
+
{normLower} - {normUpper}
@@ -105,7 +111,7 @@ const Analysis = ({
-
+
)} diff --git a/app/home/(user)/_components/dashboard.tsx b/app/home/(user)/_components/dashboard.tsx index e93ce86..7d3146b 100644 --- a/app/home/(user)/_components/dashboard.tsx +++ b/app/home/(user)/_components/dashboard.tsx @@ -3,7 +3,9 @@ import Link from 'next/link'; import { InfoTooltip } from '@/components/ui/info-tooltip'; +import type { AccountWithParams } from '@/packages/features/accounts/src/server/api'; import { BlendingModeIcon, RulerHorizontalIcon } from '@radix-ui/react-icons'; +import Isikukood from 'isikukood'; import { Activity, ChevronRight, @@ -15,7 +17,6 @@ import { TrendingUp, User, } from 'lucide-react'; -import Isikukood from 'isikukood'; import { Button } from '@kit/ui/button'; import { @@ -28,7 +29,6 @@ import { } from '@kit/ui/card'; import { Trans } from '@kit/ui/trans'; import { cn } from '@kit/ui/utils'; -import type { AccountWithParams } from '@/packages/features/accounts/src/server/api'; const cards = ({ gender, diff --git a/app/home/[account]/billing/_components/health-benefit-form.tsx b/app/home/[account]/billing/_components/health-benefit-form.tsx index 8c11bc4..a247f92 100644 --- a/app/home/[account]/billing/_components/health-benefit-form.tsx +++ b/app/home/[account]/billing/_components/health-benefit-form.tsx @@ -1,5 +1,7 @@ 'use client'; +import { useState } from 'react'; + import { UpdateHealthBenefitSchema } from '@/packages/billing/core/src/schema'; import { Database } from '@/packages/supabase/src/database.types'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -22,32 +24,52 @@ import YearlyExpensesOverview from './yearly-expenses-overview'; const HealthBenefitForm = ({ account, companyParams, + employeeCount, }: { account: Database['medreport']['Tables']['accounts']['Row']; companyParams: Database['medreport']['Tables']['company_params']['Row']; + employeeCount: number; }) => { + const [currentCompanyParams, setCurrentCompanyParams] = + useState( + companyParams, + ); + const [isLoading, setIsLoading] = useState(false); const form = useForm({ resolver: zodResolver(UpdateHealthBenefitSchema), mode: 'onChange', defaultValues: { - occurance: companyParams.benefit_occurance || 'yearly', - amount: companyParams.benefit_amount || 0, + occurance: currentCompanyParams.benefit_occurance || 'yearly', + amount: currentCompanyParams.benefit_amount || 0, }, }); + const onSubmit = (data: { occurance: string; amount: number }) => { + const promise = async () => { + setIsLoading(true); + try { + await updateHealthBenefit({ ...data, accountId: account.id }); + setCurrentCompanyParams((prev) => ({ + ...prev, + benefit_amount: data.amount, + benefit_occurance: data.occurance, + })); + } finally { + setIsLoading(false); + } + }; + + toast.promise(promise, { + success: 'Andmed uuendatud', + error: 'error', + }); + }; + return ( { - toast.promise( - () => updateHealthBenefit({ ...data, accountId: account.id }), - { - success: 'success', - error: 'error', - }, - ); - })} + onSubmit={form.handleSubmit(onSubmit)} >
@@ -61,13 +83,13 @@ const HealthBenefitForm = ({

- @@ -82,7 +104,7 @@ const HealthBenefitForm = ({

- {companyParams.benefit_amount || 0} € + {currentCompanyParams.benefit_amount || 0} €

@@ -95,8 +117,8 @@ const HealthBenefitForm = ({

diff --git a/app/home/[account]/billing/_components/yearly-expenses-overview.tsx b/app/home/[account]/billing/_components/yearly-expenses-overview.tsx index 71d6592..36a42be 100644 --- a/app/home/[account]/billing/_components/yearly-expenses-overview.tsx +++ b/app/home/[account]/billing/_components/yearly-expenses-overview.tsx @@ -21,7 +21,7 @@ const YearlyExpensesOverview = ({ case 'yearly': return (companyParams.benefit_amount / 12).toFixed(2); case 'quarterly': - return (companyParams.benefit_amount / 4).toFixed(2); + return (companyParams.benefit_amount / 3).toFixed(2); case 'monthly': return companyParams.benefit_amount.toFixed(2); default: @@ -38,7 +38,7 @@ const YearlyExpensesOverview = ({ case 'yearly': return companyParams.benefit_amount.toFixed(2); case 'quarterly': - return (companyParams.benefit_amount * 4).toFixed(2); + return (companyParams.benefit_amount * 3).toFixed(2); case 'monthly': return (companyParams.benefit_amount * 12).toFixed(2); default: @@ -80,8 +80,15 @@ const YearlyExpensesOverview = ({

-

Kokku

- 13 200,00 € +

+ +

+ + {companyParams.benefit_amount + ? companyParams.benefit_amount * employeeCount + : 0}{' '} + € +
); diff --git a/app/home/[account]/billing/error.tsx b/app/home/[account]/billing/error.tsx deleted file mode 100644 index 974e826..0000000 --- a/app/home/[account]/billing/error.tsx +++ /dev/null @@ -1,48 +0,0 @@ -'use client'; - -import { ExclamationTriangleIcon } from '@radix-ui/react-icons'; - -import { useCaptureException } from '@kit/monitoring/hooks'; -import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert'; -import { AppBreadcrumbs } from '@kit/ui/app-breadcrumbs'; -import { Button } from '@kit/ui/button'; -import { PageBody, PageHeader } from '@kit/ui/page'; -import { Trans } from '@kit/ui/trans'; - -export default function BillingErrorPage({ - error, - reset, -}: { - error: Error & { digest?: string }; - reset: () => void; -}) { - useCaptureException(error); - - return ( - <> - } /> - - -
- - - - - - - - - - - - -
- -
-
-
- - ); -} diff --git a/app/home/[account]/billing/page.tsx b/app/home/[account]/billing/page.tsx index 8342c98..a56b07a 100644 --- a/app/home/[account]/billing/page.tsx +++ b/app/home/[account]/billing/page.tsx @@ -1,3 +1,4 @@ +import { createAccountsApi } from '@/packages/features/accounts/src/server/api'; import { createTeamAccountsApi } from '@/packages/features/team-accounts/src/server/api'; import { getSupabaseServerClient } from '@/packages/supabase/src/clients/server-client'; @@ -23,13 +24,20 @@ export const generateMetadata = async () => { async function TeamAccountBillingPage({ params }: TeamAccountBillingPageProps) { const accountSlug = (await params).account; - const api = createTeamAccountsApi(getSupabaseServerClient()); + const client = getSupabaseServerClient(); + const api = createTeamAccountsApi(client); + const account = await api.getTeamAccount(accountSlug); const companyParams = await api.getTeamAccountParams(account.id); + const accounts = await api.getMembers(accountSlug); return ( - + ); } diff --git a/app/select-package/page.tsx b/app/select-package/page.tsx index 56742bd..d0fcd41 100644 --- a/app/select-package/page.tsx +++ b/app/select-package/page.tsx @@ -24,7 +24,8 @@ export const generateMetadata = async () => { }; async function SelectPackagePage() { - const { analysisPackageElements, analysisPackages, countryCode } = await loadAnalysisPackages(); + const { analysisPackageElements, analysisPackages, countryCode } = + await loadAnalysisPackages(); return (
@@ -44,7 +45,10 @@ async function SelectPackagePage() { } />
- +
- - - ); -} diff --git a/packages/features/auth/src/server/api.ts b/packages/features/auth/src/server/api.ts index 30ae995..6f8ead2 100644 --- a/packages/features/auth/src/server/api.ts +++ b/packages/features/auth/src/server/api.ts @@ -2,7 +2,17 @@ import { SupabaseClient } from '@supabase/supabase-js'; import { Database } from '@kit/supabase/database'; -import { AccountSubmitData } from './actions/update-account-actions'; +export interface AccountSubmitData { + firstName: string; + lastName: string; + personalCode: string; + email: string; + phone?: string; + city?: string; + weight: number | null; + height: number | null; + userConsent: boolean; +} /** * Class representing an API for interacting with user accounts. diff --git a/packages/features/notifications/src/components/success-notification.tsx b/packages/features/notifications/src/components/success-notification.tsx index 2702708..e961dfa 100644 --- a/packages/features/notifications/src/components/success-notification.tsx +++ b/packages/features/notifications/src/components/success-notification.tsx @@ -1,5 +1,5 @@ import Image from 'next/image'; -import Link from 'next/link'; +import { redirect } from 'next/navigation'; import { MedReportLogo } from '@/components/med-report-logo'; @@ -56,10 +56,11 @@ export const SuccessNotification = ({
)} {buttonProps && ( - )}
diff --git a/packages/features/team-accounts/src/server/api.ts b/packages/features/team-accounts/src/server/api.ts index 442735d..60339ff 100644 --- a/packages/features/team-accounts/src/server/api.ts +++ b/packages/features/team-accounts/src/server/api.ts @@ -295,6 +295,20 @@ export class TeamAccountsApi { return data; } + + async getMembers(accountSlug: string) { + const members = await this.client + .schema('medreport') + .rpc('get_account_members', { + account_slug: accountSlug, + }); + + if (members.error) { + throw members.error; + } + + return members.data; + } } export function createTeamAccountsApi(client: SupabaseClient) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c457fa7..710f116 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18669,8 +18669,8 @@ snapshots: '@typescript-eslint/parser': 8.33.1(eslint@8.10.0)(typescript@5.8.3) eslint: 8.10.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0))(eslint@8.10.0) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@8.10.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.10.0) eslint-plugin-react: 7.37.5(eslint@8.10.0) eslint-plugin-react-hooks: 5.2.0(eslint@8.10.0) @@ -18689,8 +18689,8 @@ snapshots: '@typescript-eslint/parser': 8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3) eslint: 9.28.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.28.0(jiti@2.4.2)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.28.0(jiti@2.4.2)) eslint-plugin-react: 7.37.5(eslint@9.28.0(jiti@2.4.2)) eslint-plugin-react-hooks: 5.2.0(eslint@9.28.0(jiti@2.4.2)) @@ -18715,22 +18715,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)): - dependencies: - '@nolyfill/is-core-module': 1.0.39 - debug: 4.4.1 - eslint: 9.28.0(jiti@2.4.2) - get-tsconfig: 4.10.1 - is-bun-module: 2.0.0 - stable-hash: 0.0.5 - tinyglobby: 0.2.14 - unrs-resolver: 1.7.11 - optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)) - transitivePeerDependencies: - - supports-color - - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@8.10.0): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.1 @@ -18741,33 +18726,48 @@ snapshots: tinyglobby: 0.2.14 unrs-resolver: 1.7.11 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0))(eslint@8.10.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0))(eslint@8.10.0): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@9.28.0(jiti@2.4.2)): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.1 + eslint: 9.28.0(jiti@2.4.2) + get-tsconfig: 4.10.1 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.14 + unrs-resolver: 1.7.11 + optionalDependencies: + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.10.0): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.33.1(eslint@8.10.0)(typescript@5.8.3) eslint: 8.10.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@8.10.0) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.28.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3) eslint: 9.28.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.28.0(jiti@2.4.2)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0))(eslint@8.10.0): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -18776,9 +18776,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.10.0 + eslint: 9.28.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0))(eslint@8.10.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.28.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -18790,13 +18790,13 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.33.1(eslint@8.10.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -18805,9 +18805,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.28.0(jiti@2.4.2) + eslint: 8.10.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.10.0) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -18819,7 +18819,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.33.1(eslint@8.10.0)(typescript@5.8.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack diff --git a/public/locales/et/billing.json b/public/locales/et/billing.json index a071540..d89d3b3 100644 --- a/public/locales/et/billing.json +++ b/public/locales/et/billing.json @@ -137,6 +137,7 @@ "title": "Kulude ülevaade 2025 aasta raames", "monthly": "Kulu töötaja kohta kuus *", "yearly": "Maksimaalne kulu inimese kohta kokku aastas *", - "total": "Maksimaalne kulu {{employeeCount}} töötaja kohta aastas *" + "total": "Maksimaalne kulu {{employeeCount}} töötaja kohta aastas *", + "sum": "Kokku" } } diff --git a/supabase/migrations/20250811174200_delete_employee.sql b/supabase/migrations/20250811174200_delete_employee.sql new file mode 100644 index 0000000..82056e9 --- /dev/null +++ b/supabase/migrations/20250811174200_delete_employee.sql @@ -0,0 +1,3 @@ +grant + execute on function medreport.can_action_account_member (uuid, uuid) to authenticated, + service_role; \ No newline at end of file diff --git a/supabase/migrations/20250812143800_log_company_params.sql b/supabase/migrations/20250812143800_log_company_params.sql new file mode 100644 index 0000000..4641799 --- /dev/null +++ b/supabase/migrations/20250812143800_log_company_params.sql @@ -0,0 +1,189 @@ +CREATE OR REPLACE FUNCTION medreport.insert_company_params_on_new_company() +RETURNS trigger +LANGUAGE plpgsql +AS $function$ +BEGIN + IF (TG_OP = 'INSERT' AND NEW.slug IS NOT NULL) THEN + INSERT INTO medreport.company_params ( + account_id, + slug, + benefit_occurance, + benefit_amount + ) VALUES ( + NEW.id, + NEW.slug, + NULL, + NULL + ); + END IF; + + RETURN NEW; +END; +$function$ +; + +grant execute on function medreport.insert_company_params_on_new_company() to authenticated, +service_role; + +CREATE OR REPLACE FUNCTION log_company_params_changes() +RETURNS trigger AS $$ +BEGIN + -- For INSERT operation + IF (TG_OP = 'INSERT') THEN + INSERT INTO audit.log_entries ( + schema_name, + table_name, + record_key, + operation, + row_data, + changed_data, + changed_by, + changed_by_role, + changed_at + ) + VALUES ( + 'medreport', -- Schema name + 'company_params', -- Table name + NEW.id, -- The ID of the inserted row + 'INSERT', -- Operation type + NULL, -- No old data for INSERT + row_to_json(NEW), -- New data (after the INSERT) + auth.uid(), -- The user performing the insert + SESSION_USER, -- The role performing the insert + CURRENT_TIMESTAMP -- Timestamp of the insert + ); + -- For UPDATE operation + ELSIF (TG_OP = 'UPDATE') THEN + INSERT INTO audit.log_entries ( + schema_name, + table_name, + record_key, + operation, + row_data, + changed_data, + changed_by, + changed_by_role, + changed_at + ) + VALUES ( + 'medreport', -- Schema name + 'company_params', -- Table name + OLD.id, -- The ID of the updated row + 'UPDATE', -- Operation type + row_to_json(OLD), -- Old data (before the update) + row_to_json(NEW), -- New data (after the update) + auth.uid(), -- The user performing the update + SESSION_USER, -- The role performing the update + CURRENT_TIMESTAMP -- Timestamp of the update + ); + -- For DELETE operation + ELSIF (TG_OP = 'DELETE') THEN + INSERT INTO audit.log_entries ( + schema_name, + table_name, + record_key, + operation, + row_data, + changed_data, + changed_by, + changed_by_role, + changed_at + ) + VALUES ( + 'medreport', -- Schema name + 'company_params', -- Table name + OLD.id, -- The ID of the deleted row + 'DELETE', -- Operation type + row_to_json(OLD), -- Old data (before the delete) + NULL, -- No new data for DELETE + auth.uid(), -- The user performing the delete + SESSION_USER, -- The role performing the delete + CURRENT_TIMESTAMP -- Timestamp of the delete + ); + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER company_params_audit_trigger +AFTER INSERT OR UPDATE OR DELETE ON medreport.company_params +FOR EACH ROW +EXECUTE FUNCTION log_company_params_changes(); + +create or replace function medreport.create_team_account ( + account_name text, + new_personal_code text +) returns medreport.accounts + security definer + set search_path = '' +as $$ +declare + existing_account medreport.accounts; + current_user uuid := (select auth.uid())::uuid; + new_account medreport.accounts; +begin + if not medreport.is_set('enable_team_accounts') then + raise exception 'Team accounts are not enabled'; + end if; + + -- Try to find existing account + select * + into existing_account + from medreport.accounts + where personal_code = new_personal_code + limit 1; + + -- If not found, fail + if not found then + raise exception 'No account found with personal_code = %', new_personal_code; + end if; + + insert into medreport.accounts( + name, + is_personal_account, + primary_owner_user_id) + values ( + account_name, + false, + existing_account.id) + returning + * into new_account; + + -- Insert membership + insert into medreport.accounts_memberships ( + user_id, + account_id, + account_role, + created_by, + updated_by, + created_at, + updated_at, + has_seen_confirmation + ) + values ( + existing_account.id, + new_account.id, + 'owner', + null, + null, + now(), + now(), + false + ) + on conflict do nothing; + + return new_account; +end; +$$ language plpgsql; + +grant execute on function medreport.create_team_account (text, text) to authenticated, service_role; + +ALTER TABLE "medreport"."accounts" +DROP CONSTRAINT "accounts_primary_owner_user_id_fkey"; + +ALTER TABLE "medreport"."accounts" +ADD CONSTRAINT "accounts_primary_owner_user_id_fkey" +FOREIGN KEY (primary_owner_user_id) +REFERENCES auth.users(id) +ON DELETE CASCADE;