From 3295ce7d047768f8e5b2492bc9f304e257203d2a Mon Sep 17 00:00:00 2001 From: Danel Kungla Date: Tue, 12 Aug 2025 15:47:01 +0300 Subject: [PATCH] feat(auth): refactor account submission data structure and remove unused billing error component --- .../_lib/server/update-account.ts | 13 +- app/home/[account]/billing/error.tsx | 48 ----- packages/features/auth/src/server/api.ts | 12 +- .../20250812143800_log_company_params.sql | 189 ++++++++++++++++++ 4 files changed, 202 insertions(+), 60 deletions(-) delete mode 100644 app/home/[account]/billing/error.tsx create mode 100644 supabase/migrations/20250812143800_log_company_params.sql diff --git a/app/auth/update-account/_lib/server/update-account.ts b/app/auth/update-account/_lib/server/update-account.ts index 9de3586..a7a424a 100644 --- a/app/auth/update-account/_lib/server/update-account.ts +++ b/app/auth/update-account/_lib/server/update-account.ts @@ -4,22 +4,13 @@ import { redirect } from 'next/navigation'; import { updateCustomer } from '@lib/data/customer'; +import { AccountSubmitData, createAuthApi } from '@kit/auth/api'; import { enhanceAction } from '@kit/next/actions'; import { getSupabaseServerClient } from '@kit/supabase/server-client'; import pathsConfig from '~/config/paths.config'; -export interface AccountSubmitData { - firstName: string; - lastName: string; - personalCode: string; - email: string; - phone?: string; - city?: string; - weight: number | null; - height: number | null; - userConsent: boolean; -} +import { UpdateAccountSchema } from '../schemas/update-account.schema'; export const onUpdateAccount = enhanceAction( async (params: AccountSubmitData) => { 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/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/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;