feat: create email template for TTO reservation confirmation

feat: implement order notifications service with TTO reservation confirmation handling

feat: create migration for TTO booking email webhook trigger
This commit is contained in:
Danel Kungla
2025-09-30 16:05:43 +03:00
parent 4003284f3a
commit 72f6f2b716
56 changed files with 3692 additions and 294 deletions

View File

@@ -1,5 +1,6 @@
import { getSupabaseServerClient } from "@/packages/supabase/src/clients/server-client";
import { loadCompanyPersonalAccountsBalanceEntries } from "./load-team-account-benefit-statistics";
import { getSupabaseServerClient } from '@/packages/supabase/src/clients/server-client';
import { loadCompanyPersonalAccountsBalanceEntries } from './load-team-account-benefit-statistics';
export interface TeamAccountBenefitExpensesOverview {
benefitAmount: number | null;
@@ -10,7 +11,7 @@ export interface TeamAccountBenefitExpensesOverview {
total: number;
}
const MANAGEMENT_FEE = 5.50;
const MANAGEMENT_FEE = 5.5;
const MONTHS = 12;
const QUARTERS = 4;
@@ -32,15 +33,19 @@ export async function loadTeamAccountBenefitExpensesOverview({
.single();
let benefitAmount: TeamAccountBenefitExpensesOverview['benefitAmount'] = null;
let benefitOccurrence: TeamAccountBenefitExpensesOverview['benefitOccurrence'] = null;
let benefitOccurrence: TeamAccountBenefitExpensesOverview['benefitOccurrence'] =
null;
if (error) {
console.warn('Failed to load team account benefit expenses overview');
} else {
benefitAmount = data.benefit_amount as TeamAccountBenefitExpensesOverview['benefitAmount'];
benefitOccurrence = data.benefit_occurrence as TeamAccountBenefitExpensesOverview['benefitOccurrence'];
benefitAmount =
data.benefit_amount as TeamAccountBenefitExpensesOverview['benefitAmount'];
benefitOccurrence =
data.benefit_occurrence as TeamAccountBenefitExpensesOverview['benefitOccurrence'];
}
const { purchaseEntriesTotal } = await loadCompanyPersonalAccountsBalanceEntries({ accountId: companyId });
const { purchaseEntriesTotal } =
await loadCompanyPersonalAccountsBalanceEntries({ accountId: companyId });
return {
benefitAmount,
@@ -55,7 +60,8 @@ export async function loadTeamAccountBenefitExpensesOverview({
const currentDate = new Date();
const createdAt = new Date(data.created_at);
const isCreatedThisYear = createdAt.getFullYear() === currentDate.getFullYear();
const isCreatedThisYear =
createdAt.getFullYear() === currentDate.getFullYear();
if (benefitOccurrence === 'yearly') {
return benefitAmount * employeeCount;
} else if (benefitOccurrence === 'monthly') {
@@ -71,5 +77,5 @@ export async function loadTeamAccountBenefitExpensesOverview({
}
return 0;
})(),
}
};
}

View File

@@ -16,7 +16,7 @@ export interface AccountBenefitStatistics {
analysisPackagesCount: number;
analysisPackagesSum: number;
}
};
}
export const loadCompanyPersonalAccountsBalanceEntries = async ({
@@ -38,12 +38,21 @@ export const loadCompanyPersonalAccountsBalanceEntries = async ({
.from('account_balance_entries')
.select('*')
.eq('is_active', true)
.in('account_id', accountMemberships.map(({ user_id }) => user_id))
.in(
'account_id',
accountMemberships.map(({ user_id }) => user_id),
)
.throwOnError();
const purchaseEntries = accountBalanceEntries.filter(({ entry_type }) => entry_type === 'purchase');
const analysesEntries = purchaseEntries.filter(({ is_analysis_order }) => is_analysis_order);
const analysisPackagesEntries = purchaseEntries.filter(({ is_analysis_package_order }) => is_analysis_package_order);
const purchaseEntries = accountBalanceEntries.filter(
({ entry_type }) => entry_type === 'purchase',
);
const analysesEntries = purchaseEntries.filter(
({ is_analysis_order }) => is_analysis_order,
);
const analysisPackagesEntries = purchaseEntries.filter(
({ is_analysis_package_order }) => is_analysis_package_order,
);
return {
accountBalanceEntries,
@@ -51,9 +60,12 @@ export const loadCompanyPersonalAccountsBalanceEntries = async ({
analysisPackagesEntries,
companyAccountsCount: count || 0,
purchaseEntries,
purchaseEntriesTotal: purchaseEntries.reduce((acc, { amount }) => acc + Math.abs(amount || 0), 0),
purchaseEntriesTotal: purchaseEntries.reduce(
(acc, { amount }) => acc + Math.abs(amount || 0),
0,
),
};
}
};
export const loadAccountBenefitStatistics = async (
accountId: string,
@@ -86,10 +98,16 @@ export const loadAccountBenefitStatistics = async (
totalSum: purchaseEntriesTotal,
analysesCount: analysesEntries.length,
analysesSum: analysesEntries.reduce((acc, { amount }) => acc + Math.abs(amount || 0), 0),
analysesSum: analysesEntries.reduce(
(acc, { amount }) => acc + Math.abs(amount || 0),
0,
),
analysisPackagesCount: analysisPackagesEntries.length,
analysisPackagesSum: analysisPackagesEntries.reduce((acc, { amount }) => acc + Math.abs(amount || 0), 0),
analysisPackagesSum: analysisPackagesEntries.reduce(
(acc, { amount }) => acc + Math.abs(amount || 0),
0,
),
},
};
};

View File

@@ -4,6 +4,8 @@ import { Database } from '@/packages/supabase/src/database.types';
import Isikukood from 'isikukood';
import { Clock, TrendingUp, User } from 'lucide-react';
import type { BmiThresholds } from '@kit/accounts/types/accounts';
import {
bmiFromMetric,
getBmiBackgroundColor,
@@ -11,7 +13,6 @@ import {
} from '~/lib/utils';
import { TeamAccountStatisticsProps } from '../../_components/team-account-statistics';
import type { BmiThresholds } from '@kit/accounts/types/accounts';
interface AccountHealthDetailsField {
title: string;