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} client - The Supabase client instance. */ class AccountsApi { constructor(private readonly client: SupabaseClient) {} /** * @name getAccount * @description Get the account data for the given ID. * @param id */ async getAccount(id: string): Promise { 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 { 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 { 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 { 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) { return new AccountsApi(client); }