Files
medreport_mrb2b/packages/features/accounts/src/server/api.ts

343 lines
8.2 KiB
TypeScript

import { SupabaseClient } from '@supabase/supabase-js';
import { Database } from '@kit/supabase/database';
import { AnalysisResultDetails, UserAnalysis } from '../types/accounts';
import PersonalCode from '~/lib/utils';
export type AccountWithParams =
Database['medreport']['Tables']['accounts']['Row'] & {
accountParams:
| (Pick<
Database['medreport']['Tables']['account_params']['Row'],
'weight' | 'height'
> & {
isSmoker:
| Database['medreport']['Tables']['account_params']['Row']['is_smoker']
| null;
})
| null;
};
/**
* Class representing an API for interacting with user accounts.
* @constructor
* @param {SupabaseClient<Database>} client - The Supabase client instance.
*/
class AccountsApi {
constructor(private readonly client: SupabaseClient<Database>) {}
/**
* @name getAccount
* @description Get the account data for the given ID.
* @param id
*/
async getAccount(id: string): Promise<AccountWithParams> {
const { data, error } = await this.client
.schema('medreport')
.from('accounts')
.select(
'*, accountParams: account_params (weight, height, isSmoker:is_smoker)',
)
.eq('id', id)
.single();
if (error) {
throw error;
}
return data;
}
/**
* @name getPersonalAccountByUserId
* @description Get the personal account data for the given user ID.
* @param userId
*/
async getPersonalAccountByUserId(userId: string): Promise<AccountWithParams> {
const { data, error } = await this.client
.schema('medreport')
.from('accounts')
.select(
'*, accountParams: account_params (weight, height, isSmoker:is_smoker)',
)
.eq('primary_owner_user_id', userId)
.eq('is_personal_account', true)
.single();
if (error) {
throw error;
}
const { personal_code, ...rest } = data;
return {
...rest,
personal_code: PersonalCode.getPersonalCode(personal_code),
};
}
/**
* @name getAccountWorkspace
* @description Get the account workspace data.
*/
async getAccountWorkspace() {
const { data, error } = await this.client
.schema('medreport')
.from('user_account_workspace')
.select(`*`)
.single();
if (error) {
throw error;
}
return data;
}
/**
* @name loadUserAccounts
* Load only user-owned accounts (not just memberships).
*/
async loadUserAccounts() {
const authUser = await this.client.auth.getUser();
const { data, error: userError } = authUser;
if (userError) {
console.error('Failed to get user', userError);
throw userError;
}
const { user } = data;
const { data: accounts, error } = await this.client
.schema('medreport')
.from('accounts_memberships')
.select(
`
account_id,
accounts (
name,
slug,
picture_url,
application_role
)
`,
)
.eq('user_id', user.id)
.eq('account_role', 'owner');
if (error) {
console.error('error', error);
throw error;
}
return accounts.map(({ accounts }) => ({
label: accounts.name,
value: accounts.slug,
image: accounts.picture_url,
application_role: accounts.application_role,
}));
}
async loadTempUserAccounts() {
const { data: accounts, error } = await this.client
.schema('medreport')
.from('user_accounts')
.select(`name, slug`);
if (error) {
throw error;
}
return accounts.map(({ name, slug }) => {
return {
label: name,
value: slug,
};
});
}
/**
* @name getSubscription
* Get the subscription data for the given user.
* @param accountId
*/
async getSubscription(accountId: string) {
const response = await this.client
.schema('medreport')
.from('subscriptions')
.select('*, items: subscription_items !inner (*)')
.eq('account_id', accountId)
.maybeSingle();
if (response.error) {
throw response.error;
}
return response.data;
}
/**
* Get the orders data for the given account.
* @param accountId
*/
async getOrder(accountId: string) {
const response = await this.client
.schema('medreport')
.from('orders')
.select('*, items: order_items !inner (*)')
.eq('account_id', accountId)
.maybeSingle();
if (response.error) {
throw response.error;
}
return response.data;
}
/**
* @name getCustomerId
* Get the billing customer ID for the given user.
* If the user does not have a billing customer ID, it will return null.
* @param accountId
*/
async getCustomerId(accountId: string) {
const response = await this.client
.schema('medreport')
.from('billing_customers')
.select('customer_id')
.eq('account_id', accountId)
.maybeSingle();
if (response.error) {
throw response.error;
}
return response.data?.customer_id;
}
async getUserAnalysis(
analysisOrderId: number,
): Promise<AnalysisResultDetails | null> {
const authUser = await this.client.auth.getUser();
const { data, error: userError } = authUser;
if (userError) {
console.error('Failed to get user', userError);
throw userError;
}
const { user } = data;
const { data: analysisResponse } = await this.client
.schema('medreport')
.from('analysis_responses')
.select(
`*,
elements:analysis_response_elements(analysis_name,norm_status,response_value,unit,norm_lower_included,norm_upper_included,norm_lower,norm_upper,response_time),
order:analysis_order_id(medusa_order_id, status, created_at),
summary:analysis_order_id(doctor_analysis_feedback(*))`,
)
.eq('user_id', user.id)
.eq('analysis_order_id', analysisOrderId)
.throwOnError();
const responseWithElements = analysisResponse?.[0];
if (!responseWithElements) {
return null;
}
const feedback = responseWithElements.summary.doctor_analysis_feedback?.[0];
return {
...responseWithElements,
summary:
feedback?.status === 'COMPLETED'
? responseWithElements.summary.doctor_analysis_feedback?.[0]
: null,
};
}
async getUserAnalyses(): Promise<UserAnalysis | null> {
const authUser = await this.client.auth.getUser();
const { data, error: userError } = authUser;
if (userError) {
console.error('Failed to get user', userError);
throw userError;
}
const { user } = data;
const { data: analysisResponses } = await this.client
.schema('medreport')
.from('analysis_responses')
.select('*')
.eq('user_id', user.id);
if (!analysisResponses) {
return null;
}
const analysisResponseIds = analysisResponses.map((r) => r.id);
const { data: analysisResponseElements } = await this.client
.schema('medreport')
.from('analysis_response_elements')
.select('*')
.in('analysis_response_id', analysisResponseIds);
if (!analysisResponseElements) {
return null;
}
return analysisResponses.map((r) => ({
...r,
elements: analysisResponseElements.filter(
(e) => e.analysis_response_id === r.id,
),
}));
}
async hasAccountTeamMembership(accountId?: string) {
if (!accountId) {
return false;
}
const { count, error } = await this.client
.schema('medreport')
.from('accounts_memberships')
.select('account_id', { count: 'exact', head: true })
.eq('account_id', accountId);
if (error) {
throw error;
}
return (count ?? 0) > 0;
}
async fetchBmiThresholds() {
// Fetch BMI
const { data, error } = await this.client
.schema('medreport')
.from('bmi_thresholds')
.select(
'age_min,age_max,underweight_max,normal_min,normal_max,overweight_min,strong_min,obesity_min',
)
.order('age_min', { ascending: true });
if (error) {
console.error('Error fetching BMI thresholds:', error);
throw error;
}
return data;
}
}
export function createAccountsApi(client: SupabaseClient<Database>) {
return new AccountsApi(client);
}