add health benefit form
fix super admin
This commit is contained in:
2
.env
2
.env
@@ -35,7 +35,7 @@ NEXT_PUBLIC_ENABLE_THEME_TOGGLE=true
|
||||
NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_DELETION=true
|
||||
NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_BILLING=false
|
||||
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_DELETION=false
|
||||
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_BILLING=false
|
||||
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_BILLING=true
|
||||
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS=true
|
||||
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_CREATION=true
|
||||
NEXT_PUBLIC_LANGUAGE_PRIORITY=application
|
||||
|
||||
@@ -85,5 +85,11 @@ To access admin pages follow these steps:
|
||||
|
||||
- Register new user
|
||||
- Go to Profile and add Multi-Factor Authentication
|
||||
- Sign out and Sign in
|
||||
- Authenticate with mfa (at current time profile page prompts it again)
|
||||
- update your role. look at `supabase/sql/super-admin.sql`
|
||||
- Sign out and Sign in
|
||||
|
||||
## Company User
|
||||
|
||||
- With admin account go to `http://localhost:3000/admin/accounts`
|
||||
- For Create Company Account to work you need to have rows in `medreport.roles` table. For that you can sql in `supabase/sql/super-admin.sql`
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { UserWorkspace } from '@/app/home/(user)/_lib/server/load-user-workspace';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { PersonalAccountDropdown } from '@kit/accounts/personal-account-dropdown';
|
||||
@@ -29,7 +30,11 @@ const features = {
|
||||
enableThemeToggle: featuresFlagConfig.enableThemeToggle,
|
||||
};
|
||||
|
||||
export function SiteHeaderAccountSection() {
|
||||
export function SiteHeaderAccountSection({
|
||||
accounts,
|
||||
}: {
|
||||
accounts: UserWorkspace['accounts'];
|
||||
}) {
|
||||
const session = useSession();
|
||||
const signOut = useSignOut();
|
||||
|
||||
@@ -41,6 +46,7 @@ export function SiteHeaderAccountSection() {
|
||||
features={features}
|
||||
user={session.data.user}
|
||||
signOutRequested={() => signOut.mutateAsync()}
|
||||
accounts={accounts}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { UserWorkspace } from '@/app/home/(user)/_lib/server/load-user-workspace';
|
||||
|
||||
import { Header } from '@kit/ui/marketing';
|
||||
|
||||
import { AppLogo } from '~/components/app-logo';
|
||||
@@ -5,12 +7,16 @@ import { AppLogo } from '~/components/app-logo';
|
||||
import { SiteHeaderAccountSection } from './site-header-account-section';
|
||||
import { SiteNavigation } from './site-navigation';
|
||||
|
||||
export function SiteHeader() {
|
||||
export function SiteHeader({
|
||||
accounts,
|
||||
}: {
|
||||
accounts: UserWorkspace['accounts'];
|
||||
}) {
|
||||
return (
|
||||
<Header
|
||||
logo={<AppLogo />}
|
||||
navigation={<SiteNavigation />}
|
||||
actions={<SiteHeaderAccountSection />}
|
||||
actions={<SiteHeaderAccountSection accounts={accounts} />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import { use } from 'react';
|
||||
|
||||
import { SiteFooter } from '~/(marketing)/_components/site-footer';
|
||||
import { SiteHeader } from '~/(marketing)/_components/site-header';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
import { loadUserWorkspace } from '../home/(user)/_lib/server/load-user-workspace';
|
||||
|
||||
function SiteLayout(props: React.PropsWithChildren) {
|
||||
const workspace = use(loadUserWorkspace());
|
||||
|
||||
return (
|
||||
<div className={'flex min-h-[100vh] flex-col'}>
|
||||
<SiteHeader />
|
||||
<SiteHeader accounts={workspace.accounts} />
|
||||
|
||||
{props.children}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
import { UserWorkspace } from '@/app/home/(user)/_lib/server/load-user-workspace';
|
||||
import { LayoutDashboard, Users } from 'lucide-react';
|
||||
|
||||
import {
|
||||
@@ -21,10 +22,13 @@ import {
|
||||
import { AppLogo } from '~/components/app-logo';
|
||||
import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container';
|
||||
|
||||
export function AdminSidebar() {
|
||||
export function AdminSidebar({
|
||||
accounts,
|
||||
}: {
|
||||
accounts: UserWorkspace['accounts'];
|
||||
}) {
|
||||
const path = usePathname();
|
||||
const { open } = useSidebar();
|
||||
|
||||
return (
|
||||
<Sidebar collapsible="icon">
|
||||
<SidebarHeader className={'m-2'}>
|
||||
@@ -62,7 +66,7 @@ export function AdminSidebar() {
|
||||
</SidebarContent>
|
||||
|
||||
<SidebarFooter>
|
||||
<ProfileAccountDropdownContainer />
|
||||
<ProfileAccountDropdownContainer accounts={accounts} />
|
||||
</SidebarFooter>
|
||||
</Sidebar>
|
||||
);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ServerDataLoader } from '@makerkit/data-loader-supabase-nextjs';
|
||||
|
||||
import { AdminAccountsTable } from '@kit/admin/components/admin-accounts-table';
|
||||
import { AdminCreateUserDialog } from '@kit/admin/components/admin-create-user-dialog';
|
||||
import { AdminCreateCompanyDialog } from '@kit/admin/components/admin-create-company-dialog';
|
||||
import { AdminCreateUserDialog } from '@kit/admin/components/admin-create-user-dialog';
|
||||
import { AdminGuard } from '@kit/admin/components/admin-guard';
|
||||
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||
import { AppBreadcrumbs } from '@kit/ui/app-breadcrumbs';
|
||||
@@ -24,7 +24,7 @@ export const metadata = {
|
||||
};
|
||||
|
||||
async function AccountsPage(props: AdminAccountsPageProps) {
|
||||
const client = getSupabaseServerClient();
|
||||
const client = getSupabaseServerClient().schema('medreport');
|
||||
const searchParams = await props.searchParams;
|
||||
const page = searchParams.page ? parseInt(searchParams.page) : 1;
|
||||
|
||||
@@ -33,18 +33,19 @@ async function AccountsPage(props: AdminAccountsPageProps) {
|
||||
<PageHeader description={<AppBreadcrumbs />}>
|
||||
<div className="flex justify-end gap-2">
|
||||
<AdminCreateUserDialog>
|
||||
<Button data-test="admin-create-user-button">Create Personal Account</Button>
|
||||
<Button data-test="admin-create-user-button">
|
||||
Create Personal Account
|
||||
</Button>
|
||||
</AdminCreateUserDialog>
|
||||
<AdminCreateCompanyDialog>
|
||||
<Button>Create Company Account</Button>
|
||||
</AdminCreateCompanyDialog>
|
||||
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<PageBody>
|
||||
<ServerDataLoader
|
||||
table={'accounts'}
|
||||
table="accounts"
|
||||
client={client}
|
||||
page={page}
|
||||
where={(queryBuilder) => {
|
||||
@@ -55,7 +56,9 @@ async function AccountsPage(props: AdminAccountsPageProps) {
|
||||
}
|
||||
|
||||
if (query) {
|
||||
queryBuilder.or(`name.ilike.%${query}%,email.ilike.%${query}%,personal_code.ilike.%${query}%`);
|
||||
queryBuilder.or(
|
||||
`name.ilike.%${query}%,email.ilike.%${query}%,personal_code.ilike.%${query}%`,
|
||||
);
|
||||
}
|
||||
|
||||
return queryBuilder;
|
||||
|
||||
@@ -8,6 +8,8 @@ import { SidebarProvider } from '@kit/ui/shadcn-sidebar';
|
||||
import { AdminSidebar } from '~/admin/_components/admin-sidebar';
|
||||
import { AdminMobileNavigation } from '~/admin/_components/mobile-navigation';
|
||||
|
||||
import { loadUserWorkspace } from '../home/(user)/_lib/server/load-user-workspace';
|
||||
|
||||
export const metadata = {
|
||||
title: `Super Admin`,
|
||||
};
|
||||
@@ -16,12 +18,13 @@ export const dynamic = 'force-dynamic';
|
||||
|
||||
export default function AdminLayout(props: React.PropsWithChildren) {
|
||||
const state = use(getLayoutState());
|
||||
const workspace = use(loadUserWorkspace());
|
||||
|
||||
return (
|
||||
<SidebarProvider defaultOpen={state.open}>
|
||||
<Page style={'sidebar'}>
|
||||
<PageNavigation>
|
||||
<AdminSidebar />
|
||||
<AdminSidebar accounts={workspace.accounts} />
|
||||
</PageNavigation>
|
||||
|
||||
<PageMobileNavigation>
|
||||
|
||||
@@ -8,6 +8,8 @@ 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 {
|
||||
@@ -21,9 +23,6 @@ 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),
|
||||
|
||||
@@ -2,6 +2,7 @@ import { use } from 'react';
|
||||
|
||||
import { cookies } from 'next/headers';
|
||||
|
||||
import { retrieveCart } from '@lib/data';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { UserWorkspaceContextProvider } from '@kit/accounts/components';
|
||||
@@ -17,7 +18,6 @@ import { HomeMenuNavigation } from '../_components/home-menu-navigation';
|
||||
import { HomeMobileNavigation } from '../_components/home-mobile-navigation';
|
||||
import { HomeSidebar } from '../_components/home-sidebar';
|
||||
import { loadUserWorkspace } from '../_lib/server/load-user-workspace';
|
||||
import { retrieveCart } from '@lib/data';
|
||||
|
||||
function UserHomeLayout({ children }: React.PropsWithChildren) {
|
||||
const state = use(getLayoutState());
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import {
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@kit/ui/form';
|
||||
import { Input } from '@kit/ui/input';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@kit/ui/select';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
const HealthBenefitFields = () => {
|
||||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
<FormField
|
||||
name="occurance"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey="billing:healthBenefitForm.title" />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Select {...field} onValueChange={field.onChange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder={<Trans i18nKey="common:formField:occurance" />}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="yearly">
|
||||
<Trans i18nKey="billing:occurance.yearly" />
|
||||
</SelectItem>
|
||||
<SelectItem value="quarterly">
|
||||
<Trans i18nKey="billing:occurance.quarterly" />
|
||||
</SelectItem>
|
||||
<SelectItem value="monthly">
|
||||
<Trans i18nKey="billing:occurance.monthly" />
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="amount"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:amount'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
type="number"
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target.value === '' ? null : Number(e.target.value),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HealthBenefitFields;
|
||||
111
app/home/[account]/billing/_components/health-benefit-form.tsx
Normal file
111
app/home/[account]/billing/_components/health-benefit-form.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
'use client';
|
||||
|
||||
import { UpdateHealthBenefitSchema } from '@/packages/billing/core/src/schema';
|
||||
import { Database } from '@/packages/supabase/src/database.types';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { PiggyBankIcon } from 'lucide-react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
import { Button } from '@kit/ui/button';
|
||||
import { Form } from '@kit/ui/form';
|
||||
import { Spinner } from '@kit/ui/makerkit/spinner';
|
||||
import { Separator } from '@kit/ui/shadcn/separator';
|
||||
import { toast } from '@kit/ui/shadcn/sonner';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { cn } from '~/lib/utils';
|
||||
|
||||
import { updateHealthBenefit } from '../_lib/server/server-actions';
|
||||
import HealthBenefitFields from './health-benefit-fields';
|
||||
import YearlyExpensesOverview from './yearly-expenses-overview';
|
||||
|
||||
const HealthBenefitForm = ({
|
||||
account,
|
||||
companyParams,
|
||||
}: {
|
||||
account: Database['medreport']['Tables']['accounts']['Row'];
|
||||
companyParams: Database['medreport']['Tables']['company_params']['Row'];
|
||||
}) => {
|
||||
const form = useForm({
|
||||
resolver: zodResolver(UpdateHealthBenefitSchema),
|
||||
mode: 'onChange',
|
||||
defaultValues: {
|
||||
occurance: companyParams.benefit_occurance || 'yearly',
|
||||
amount: companyParams.benefit_amount || 0,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
className="flex flex-col gap-6 px-6 text-left"
|
||||
onSubmit={form.handleSubmit((data) => {
|
||||
toast.promise(
|
||||
() => updateHealthBenefit({ ...data, accountId: account.id }),
|
||||
{
|
||||
success: 'success',
|
||||
error: 'error',
|
||||
},
|
||||
);
|
||||
})}
|
||||
>
|
||||
<div className="mt-8 flex items-center justify-between">
|
||||
<div>
|
||||
<h4>
|
||||
<Trans
|
||||
i18nKey="billing:pageTitle"
|
||||
values={{ companyName: account.slug }}
|
||||
/>
|
||||
</h4>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
<Trans i18nKey="billing:description" />
|
||||
</p>
|
||||
</div>
|
||||
<Button type="submit" className="relative">
|
||||
{form.formState.isSubmitting && (
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
)}
|
||||
<div className={cn({ invisible: form.formState.isSubmitting })}>
|
||||
<Trans i18nKey="account:saveChanges" />
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-row gap-6">
|
||||
<div className="border-border w-1/3 rounded-lg border">
|
||||
<div className="p-6">
|
||||
<div className="flex h-16 w-16 items-center justify-center rounded-full bg-orange-100">
|
||||
<PiggyBankIcon className="h-[32px] w-[32px] stroke-orange-400 stroke-2" />
|
||||
</div>
|
||||
<p className="mt-4 text-sm font-medium">
|
||||
<Trans i18nKey="billing:healthBenefitForm.description" />
|
||||
</p>
|
||||
<p className="pt-2 text-2xl font-semibold">
|
||||
{companyParams.benefit_amount || 0} €
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="p-6">
|
||||
<HealthBenefitFields />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1">
|
||||
<YearlyExpensesOverview
|
||||
employeeCount={22}
|
||||
companyParams={companyParams}
|
||||
/>
|
||||
<p className="text-muted-foreground mt-2 text-sm">
|
||||
<Trans i18nKey="billing:healthBenefitForm.info" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default HealthBenefitForm;
|
||||
@@ -0,0 +1,90 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { Database } from '@/packages/supabase/src/database.types';
|
||||
|
||||
import { Trans } from '@kit/ui/makerkit/trans';
|
||||
import { Separator } from '@kit/ui/separator';
|
||||
|
||||
const YearlyExpensesOverview = ({
|
||||
employeeCount = 0,
|
||||
companyParams,
|
||||
}: {
|
||||
employeeCount?: number;
|
||||
companyParams: Database['medreport']['Tables']['company_params']['Row'];
|
||||
}) => {
|
||||
const monthlyExpensePerEmployee = useMemo(() => {
|
||||
if (!companyParams.benefit_amount) {
|
||||
return '0.00';
|
||||
}
|
||||
|
||||
switch (companyParams.benefit_occurance) {
|
||||
case 'yearly':
|
||||
return (companyParams.benefit_amount / 12).toFixed(2);
|
||||
case 'quarterly':
|
||||
return (companyParams.benefit_amount / 4).toFixed(2);
|
||||
case 'monthly':
|
||||
return companyParams.benefit_amount.toFixed(2);
|
||||
default:
|
||||
return '0.00';
|
||||
}
|
||||
}, [companyParams]);
|
||||
|
||||
const maxYearlyExpensePerEmployee = useMemo(() => {
|
||||
if (!companyParams.benefit_amount) {
|
||||
return '0.00';
|
||||
}
|
||||
|
||||
switch (companyParams.benefit_occurance) {
|
||||
case 'yearly':
|
||||
return companyParams.benefit_amount.toFixed(2);
|
||||
case 'quarterly':
|
||||
return (companyParams.benefit_amount * 4).toFixed(2);
|
||||
case 'monthly':
|
||||
return (companyParams.benefit_amount * 12).toFixed(2);
|
||||
default:
|
||||
return '0.00';
|
||||
}
|
||||
}, [companyParams]);
|
||||
|
||||
return (
|
||||
<div className="border-border rounded-lg border p-6">
|
||||
<h5>
|
||||
<Trans i18nKey="billing:expensesOverview.title" />
|
||||
</h5>
|
||||
<div className="mt-5 flex justify-between">
|
||||
<p className="text-sm font-medium">
|
||||
<Trans i18nKey="billing:expensesOverview.monthly" />
|
||||
</p>
|
||||
<span className="text-primary text-sm font-bold">
|
||||
{monthlyExpensePerEmployee} €
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-3 flex justify-between">
|
||||
<p className="text-sm font-medium">
|
||||
<Trans i18nKey="billing:expensesOverview.yearly" />
|
||||
</p>
|
||||
<span className="text-sm font-medium">
|
||||
{maxYearlyExpensePerEmployee} €
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-5 mb-3 flex justify-between">
|
||||
<p className="text-sm font-medium">
|
||||
<Trans
|
||||
i18nKey="billing:expensesOverview.total"
|
||||
values={{ employeeCount: employeeCount || 0 }}
|
||||
/>
|
||||
</p>
|
||||
<span className="text-sm font-medium">
|
||||
{(Number(maxYearlyExpensePerEmployee) * employeeCount).toFixed(2)} €
|
||||
</span>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="mt-4 flex justify-between">
|
||||
<p className="font-semibold">Kokku</p>
|
||||
<span className="font-semibold">13 200,00 €</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default YearlyExpensesOverview;
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
import { UpdateHealthBenefitData } from '@/packages/features/team-accounts/src/server/types';
|
||||
|
||||
import { enhanceAction } from '@kit/next/actions';
|
||||
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||
|
||||
@@ -63,3 +65,13 @@ export const createBillingPortalSession = enhanceAction(
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
export const updateHealthBenefit = enhanceAction(
|
||||
async (data: UpdateHealthBenefitData) => {
|
||||
const client = getSupabaseServerClient();
|
||||
const service = createTeamBillingService(client);
|
||||
|
||||
await service.updateHealthBenefit(data);
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'server-only';
|
||||
|
||||
import { SupabaseClient } from '@supabase/supabase-js';
|
||||
|
||||
import { UpdateHealthBenefitData } from '@/packages/features/team-accounts/src/server/types';
|
||||
import { Database } from '@/packages/supabase/src/database.types';
|
||||
import { z } from 'zod';
|
||||
|
||||
@@ -292,6 +293,25 @@ class TeamBillingService {
|
||||
return Promise.reject(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
async updateHealthBenefit(data: UpdateHealthBenefitData): Promise<void> {
|
||||
const api = createTeamAccountsApi(this.client);
|
||||
const logger = await getLogger();
|
||||
try {
|
||||
await api.updateHealthBenefit(data);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
{
|
||||
accountId: data.accountId,
|
||||
error,
|
||||
name: `billing.updateHealthBenefit`,
|
||||
},
|
||||
`Encountered an error while updating the company health benefit`,
|
||||
);
|
||||
|
||||
return Promise.reject(error as Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getCheckoutSessionReturnUrl(accountSlug: string) {
|
||||
|
||||
@@ -1,27 +1,12 @@
|
||||
import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
|
||||
import { createTeamAccountsApi } from '@/packages/features/team-accounts/src/server/api';
|
||||
import { getSupabaseServerClient } from '@/packages/supabase/src/clients/server-client';
|
||||
|
||||
import {
|
||||
BillingPortalCard,
|
||||
CurrentLifetimeOrderCard,
|
||||
CurrentSubscriptionCard,
|
||||
} from '@kit/billing-gateway/components';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
||||
import { AppBreadcrumbs } from '@kit/ui/app-breadcrumbs';
|
||||
import { If } from '@kit/ui/if';
|
||||
import { PageBody } from '@kit/ui/page';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import { cn } from '@kit/ui/utils';
|
||||
|
||||
import billingConfig from '~/config/billing.config';
|
||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
// local imports
|
||||
import { TeamAccountLayoutPageHeader } from '../_components/team-account-layout-page-header';
|
||||
import { loadTeamAccountBillingPage } from '../_lib/server/team-account-billing-page.loader';
|
||||
import { loadTeamWorkspace } from '../_lib/server/team-account-workspace.loader';
|
||||
import { TeamAccountCheckoutForm } from './_components/team-account-checkout-form';
|
||||
import { createBillingPortalSession } from './_lib/server/server-actions';
|
||||
import HealthBenefitForm from './_components/health-benefit-form';
|
||||
|
||||
interface TeamAccountBillingPageProps {
|
||||
params: Promise<{ account: string }>;
|
||||
@@ -37,99 +22,16 @@ export const generateMetadata = async () => {
|
||||
};
|
||||
|
||||
async function TeamAccountBillingPage({ params }: TeamAccountBillingPageProps) {
|
||||
const account = (await params).account;
|
||||
const workspace = await loadTeamWorkspace(account);
|
||||
const accountId = workspace.account.id;
|
||||
|
||||
const [data, customerId] = await loadTeamAccountBillingPage(accountId);
|
||||
|
||||
const canManageBilling =
|
||||
workspace.account.permissions.includes('billing.manage');
|
||||
|
||||
const Checkout = () => {
|
||||
if (!canManageBilling) {
|
||||
return <CannotManageBillingAlert />;
|
||||
}
|
||||
const accountSlug = (await params).account;
|
||||
const api = createTeamAccountsApi(getSupabaseServerClient());
|
||||
const account = await api.getTeamAccount(accountSlug);
|
||||
const companyParams = await api.getTeamAccountParams(account.id);
|
||||
|
||||
return (
|
||||
<TeamAccountCheckoutForm customerId={customerId} accountId={accountId} />
|
||||
);
|
||||
};
|
||||
|
||||
const BillingPortal = () => {
|
||||
if (!canManageBilling || !customerId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={createBillingPortalSession}>
|
||||
<input type="hidden" name={'accountId'} value={accountId} />
|
||||
<input type="hidden" name={'slug'} value={account} />
|
||||
|
||||
<BillingPortalCard />
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TeamAccountLayoutPageHeader
|
||||
account={account}
|
||||
title={<Trans i18nKey={'common:routes.billing'} />}
|
||||
description={<AppBreadcrumbs />}
|
||||
/>
|
||||
|
||||
<PageBody>
|
||||
<div
|
||||
className={cn(`flex w-full flex-col space-y-4`, {
|
||||
'max-w-2xl': data,
|
||||
})}
|
||||
>
|
||||
<If
|
||||
condition={data}
|
||||
fallback={
|
||||
<div>
|
||||
<Checkout />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{(data) => {
|
||||
if ('active' in data) {
|
||||
return (
|
||||
<CurrentSubscriptionCard
|
||||
subscription={data}
|
||||
config={billingConfig}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<CurrentLifetimeOrderCard order={data} config={billingConfig} />
|
||||
);
|
||||
}}
|
||||
</If>
|
||||
|
||||
<BillingPortal />
|
||||
</div>
|
||||
<HealthBenefitForm account={account} companyParams={companyParams} />
|
||||
</PageBody>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default withI18n(TeamAccountBillingPage);
|
||||
|
||||
function CannotManageBillingAlert() {
|
||||
return (
|
||||
<Alert variant={'warning'}>
|
||||
<ExclamationTriangleIcon className={'h-4'} />
|
||||
|
||||
<AlertTitle>
|
||||
<Trans i18nKey={'billing:cannotManageBillingAlertTitle'} />
|
||||
</AlertTitle>
|
||||
|
||||
<AlertDescription>
|
||||
<Trans i18nKey={'billing:cannotManageBillingAlertDescription'} />
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ import { cookies } from 'next/headers';
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
import { CompanyGuard, TeamAccountWorkspaceContextProvider } from '@kit/team-accounts/components';
|
||||
import {
|
||||
CompanyGuard,
|
||||
TeamAccountWorkspaceContextProvider,
|
||||
} from '@kit/team-accounts/components';
|
||||
import { Page, PageMobileNavigation, PageNavigation } from '@kit/ui/page';
|
||||
import { SidebarProvider } from '@kit/ui/shadcn-sidebar';
|
||||
|
||||
@@ -124,9 +127,10 @@ function HeaderLayout({
|
||||
/>
|
||||
</PageNavigation>
|
||||
|
||||
<PageMobileNavigation className={'flex items-center justify-between'}>
|
||||
<PageMobileNavigation
|
||||
className={'flex items-center justify-between'}
|
||||
>
|
||||
<AppLogo />
|
||||
|
||||
<div className={'flex space-x-4'}>
|
||||
<TeamAccountLayoutMobileNavigation
|
||||
userId={data.user.id}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { use } from 'react';
|
||||
|
||||
import { CompanyGuard } from '@/packages/features/team-accounts/src/components';
|
||||
|
||||
import { AppBreadcrumbs } from '@kit/ui/app-breadcrumbs';
|
||||
import { PageBody } from '@kit/ui/page';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
@@ -9,7 +11,6 @@ import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
import { DashboardDemo } from './_components/dashboard-demo';
|
||||
import { TeamAccountLayoutPageHeader } from './_components/team-account-layout-page-header';
|
||||
import { CompanyGuard } from '@/packages/features/team-accounts/src/components';
|
||||
|
||||
interface TeamAccountHomePageProps {
|
||||
params: Promise<{ account: string }>;
|
||||
@@ -26,7 +27,7 @@ export const generateMetadata = async () => {
|
||||
|
||||
function TeamAccountHomePage({ params }: TeamAccountHomePageProps) {
|
||||
const account = use(params).account;
|
||||
|
||||
console.log('TeamAccountHomePage account', account);
|
||||
return (
|
||||
<>
|
||||
<TeamAccountLayoutPageHeader
|
||||
|
||||
@@ -5,3 +5,4 @@ export * from './cancel-subscription-params.schema';
|
||||
export * from './report-billing-usage.schema';
|
||||
export * from './update-subscription-params.schema';
|
||||
export * from './query-billing-usage.schema';
|
||||
export * from './update-health-benefit.schema';
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const UpdateHealthBenefitSchema = z.object({
|
||||
occurance: z
|
||||
.string({
|
||||
required_error: 'Occurance is required',
|
||||
})
|
||||
.nonempty(),
|
||||
amount: z.number({ required_error: 'Amount is required' }),
|
||||
});
|
||||
@@ -6,14 +6,9 @@ import Link from 'next/link';
|
||||
|
||||
import type { User } from '@supabase/supabase-js';
|
||||
|
||||
import {
|
||||
ChevronsUpDown,
|
||||
Home,
|
||||
LogOut,
|
||||
UserCircle,
|
||||
Shield,
|
||||
} from 'lucide-react';
|
||||
import { ChevronsUpDown, Home, LogOut, Shield, UserCircle } from 'lucide-react';
|
||||
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@kit/ui/avatar';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -27,10 +22,10 @@ import { ProfileAvatar } from '@kit/ui/profile-avatar';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import { cn } from '@kit/ui/utils';
|
||||
|
||||
import { usePersonalAccountData } from '../hooks/use-personal-account-data';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@kit/ui/avatar';
|
||||
import { toTitleCase } from '~/lib/utils';
|
||||
|
||||
import { usePersonalAccountData } from '../hooks/use-personal-account-data';
|
||||
|
||||
const PERSONAL_ACCOUNT_SLUG = 'personal';
|
||||
|
||||
export function PersonalAccountDropdown({
|
||||
@@ -41,7 +36,7 @@ export function PersonalAccountDropdown({
|
||||
paths,
|
||||
features,
|
||||
account,
|
||||
accounts = []
|
||||
accounts = [],
|
||||
}: {
|
||||
user: User;
|
||||
|
||||
@@ -104,7 +99,8 @@ export function PersonalAccountDropdown({
|
||||
className ?? '',
|
||||
{
|
||||
['active:bg-secondary/50 items-center gap-4 rounded-md' +
|
||||
' hover:bg-secondary m-0 transition-colors border-1 rounded-md px-4 py-1 h-10']: showProfileName,
|
||||
' hover:bg-secondary m-0 h-10 rounded-md border-1 px-4 py-1 transition-colors']:
|
||||
showProfileName,
|
||||
},
|
||||
)}
|
||||
>
|
||||
@@ -127,7 +123,6 @@ export function PersonalAccountDropdown({
|
||||
>
|
||||
{toTitleCase(displayName)}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
<ChevronsUpDown
|
||||
@@ -171,7 +166,7 @@ export function PersonalAccountDropdown({
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<If condition={accounts.length > 0}>
|
||||
<span className='px-2 text-muted-foreground text-xs'>
|
||||
<span className="text-muted-foreground px-2 text-xs">
|
||||
<Trans
|
||||
i18nKey={'teams:yourTeams'}
|
||||
values={{ teamsCount: accounts.length }}
|
||||
@@ -185,12 +180,15 @@ export function PersonalAccountDropdown({
|
||||
href={`/home/${account.value}`}
|
||||
>
|
||||
<div className={'flex items-center'}>
|
||||
<Avatar className={'rounded-xs h-5 w-5 ' + account.image}>
|
||||
<AvatarImage {...(account.image && { src: account.image })} />
|
||||
<Avatar className={'h-5 w-5 rounded-xs ' + account.image}>
|
||||
<AvatarImage
|
||||
{...(account.image && { src: account.image })}
|
||||
/>
|
||||
|
||||
<AvatarFallback
|
||||
className={cn('rounded-md', {
|
||||
['bg-background']: PERSONAL_ACCOUNT_SLUG === account.value,
|
||||
['bg-background']:
|
||||
PERSONAL_ACCOUNT_SLUG === account.value,
|
||||
['group-hover:bg-background']:
|
||||
PERSONAL_ACCOUNT_SLUG !== account.value,
|
||||
})}
|
||||
@@ -199,9 +197,7 @@ export function PersonalAccountDropdown({
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
<span className={'pl-3'}>
|
||||
{account.label}
|
||||
</span>
|
||||
<span className={'pl-3'}>{account.label}</span>
|
||||
</div>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
|
||||
@@ -3,11 +3,6 @@ import { BadgeX, Ban, ShieldPlus, VenetianMask } from 'lucide-react';
|
||||
import { Tables } from '@kit/supabase/database';
|
||||
import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client';
|
||||
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||
import {
|
||||
AccountInvitationsTable,
|
||||
AccountMembersTable,
|
||||
InviteMembersDialogContainer,
|
||||
} from '@kit/team-accounts/components';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
||||
import { AppBreadcrumbs } from '@kit/ui/app-breadcrumbs';
|
||||
import { Badge } from '@kit/ui/badge';
|
||||
|
||||
@@ -22,7 +22,9 @@ class CreateTeamAccountService {
|
||||
|
||||
logger.info(ctx, `Creating new company account...`);
|
||||
|
||||
const { error, data } = await this.client.rpc('create_team_account', {
|
||||
const { error, data } = await this.client
|
||||
.schema('medreport')
|
||||
.rpc('create_team_account', {
|
||||
account_name: params.name,
|
||||
});
|
||||
|
||||
@@ -35,7 +37,7 @@ class CreateTeamAccountService {
|
||||
`Error creating company account`,
|
||||
);
|
||||
|
||||
throw new Error('Error creating company account');
|
||||
throw new Error('Error creating company account: ' + error);
|
||||
}
|
||||
|
||||
logger.info(ctx, `Company account created successfully`);
|
||||
|
||||
@@ -9,7 +9,9 @@ import { Database } from '@kit/supabase/database';
|
||||
*/
|
||||
export async function isSuperAdmin(client: SupabaseClient<Database>) {
|
||||
try {
|
||||
const { data, error } = await client.rpc('is_super_admin');
|
||||
const { data, error } = await client
|
||||
.schema('medreport')
|
||||
.rpc('is_super_admin');
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
"./captcha/server": "./src/captcha/server/index.ts",
|
||||
"./resend-email-link": "./src/components/resend-auth-link-form.tsx",
|
||||
"./lib/utils/*": "./src/lib/utils/*.ts",
|
||||
"./api": "./src/server/api.ts"
|
||||
"./api": "./src/server/api.ts",
|
||||
"./schemas/*": "./src/schemas/*.ts",
|
||||
"./actions/*": "./src/server/actions/*.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
|
||||
225
packages/features/auth/src/components/update-account-form.tsx
Normal file
225
packages/features/auth/src/components/update-account-form.tsx
Normal file
@@ -0,0 +1,225 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import { User } from '@supabase/supabase-js';
|
||||
|
||||
import { ExternalLink } from '@/public/assets/external-link';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
import { Button } from '@kit/ui/button';
|
||||
import { Checkbox } from '@kit/ui/checkbox';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@kit/ui/form';
|
||||
import { Input } from '@kit/ui/input';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { UpdateAccountSchema } from '../schemas/update-account.schema';
|
||||
import { onUpdateAccount } from '../server/actions/update-account-actions';
|
||||
|
||||
export function UpdateAccountForm({ user }: { user: User }) {
|
||||
const form = useForm({
|
||||
resolver: zodResolver(UpdateAccountSchema),
|
||||
mode: 'onChange',
|
||||
defaultValues: {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
personalCode: '',
|
||||
email: user.email,
|
||||
phone: '',
|
||||
city: '',
|
||||
weight: 0,
|
||||
height: 0,
|
||||
userConsent: false,
|
||||
},
|
||||
});
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
className="flex flex-col gap-6 px-6 pt-10 text-left"
|
||||
onSubmit={form.handleSubmit(onUpdateAccount)}
|
||||
>
|
||||
<FormField
|
||||
name="firstName"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:firstName'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="lastName"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:lastName'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="personalCode"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:personalCode'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:email'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} disabled />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="phone"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:phone'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="city"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:city'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex flex-row justify-between gap-4">
|
||||
<FormField
|
||||
name="weight"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:weight'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="kg"
|
||||
{...field}
|
||||
value={field.value ?? ''}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target.value === '' ? null : Number(e.target.value),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="height"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:height'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="cm"
|
||||
type="number"
|
||||
{...field}
|
||||
value={field.value ?? ''}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target.value === '' ? null : Number(e.target.value),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FormField
|
||||
name="userConsent"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<div className="flex flex-row items-center gap-2 pb-1">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'account:updateAccount:userConsentLabel'} />
|
||||
</FormLabel>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href={''}
|
||||
className="flex flex-row items-center gap-2 text-sm hover:underline"
|
||||
target="_blank"
|
||||
>
|
||||
<ExternalLink />
|
||||
<Trans i18nKey={'account:updateAccount:userConsentUrlTitle'} />
|
||||
</Link>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button disabled={form.formState.isSubmitting} type="submit">
|
||||
<Trans i18nKey={'account:updateAccount:button'} />
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
@@ -2,14 +2,15 @@
|
||||
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
import { updateCustomer } from '@lib/data/customer';
|
||||
|
||||
import { createAuthApi } from '@kit/auth/api';
|
||||
import { enhanceAction } from '@kit/next/actions';
|
||||
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||
|
||||
import pathsConfig from '~/config/paths.config';
|
||||
import { updateCustomer } from '@lib/data/customer';
|
||||
|
||||
import { UpdateAccountSchema } from '../schemas/update-account.schema';
|
||||
import { UpdateAccountSchema } from '../../schemas/update-account.schema';
|
||||
|
||||
export interface AccountSubmitData {
|
||||
firstName: string;
|
||||
@@ -2,6 +2,8 @@ import { SupabaseClient } from '@supabase/supabase-js';
|
||||
|
||||
import { Database } from '@kit/supabase/database';
|
||||
|
||||
import { UpdateHealthBenefitData } from './types';
|
||||
|
||||
/**
|
||||
* Class representing an API for interacting with team accounts.
|
||||
* @constructor
|
||||
@@ -254,6 +256,45 @@ export class TeamAccountsApi {
|
||||
|
||||
return invitation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name updateHealthBenefit
|
||||
* @description Update health benefits for the team account
|
||||
*/
|
||||
async updateHealthBenefit(data: UpdateHealthBenefitData) {
|
||||
const { error } = await this.client
|
||||
.schema('medreport')
|
||||
.from('company_params')
|
||||
.update({
|
||||
benefit_occurance: data.occurance,
|
||||
benefit_amount: data.amount,
|
||||
updated_at: new Date().toISOString(),
|
||||
})
|
||||
.eq('account_id', data.accountId);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @name getTeamAccountParams
|
||||
* @description Get health benefits for the team account
|
||||
*/
|
||||
async getTeamAccountParams(accountId: string) {
|
||||
const { data, error } = await this.client
|
||||
.schema('medreport')
|
||||
.from('company_params')
|
||||
.select('*')
|
||||
.eq('account_id', accountId)
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export function createTeamAccountsApi(client: SupabaseClient<Database>) {
|
||||
|
||||
5
packages/features/team-accounts/src/server/types.ts
Normal file
5
packages/features/team-accounts/src/server/types.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface UpdateHealthBenefitData {
|
||||
accountId: string;
|
||||
occurance: string;
|
||||
amount: number;
|
||||
}
|
||||
@@ -7,9 +7,14 @@ import { Database } from '@kit/supabase/database';
|
||||
* @description Check if the current user is a super admin.
|
||||
* @param client
|
||||
*/
|
||||
export async function isCompanyAdmin(client: SupabaseClient<Database>, accountSlug: string) {
|
||||
export async function isCompanyAdmin(
|
||||
client: SupabaseClient<Database>,
|
||||
accountSlug: string,
|
||||
) {
|
||||
try {
|
||||
const { data, error } = await client.rpc('is_company_admin', {
|
||||
const { data, error } = await client
|
||||
.schema('medreport')
|
||||
.rpc('is_company_admin', {
|
||||
account_slug: accountSlug,
|
||||
});
|
||||
|
||||
|
||||
@@ -9,6 +9,39 @@ export type Json =
|
||||
export type Database = {
|
||||
audit: {
|
||||
Tables: {
|
||||
cart_entries: {
|
||||
Row: {
|
||||
account_id: string
|
||||
cart_id: string
|
||||
changed_by: string
|
||||
comment: string | null
|
||||
created_at: string
|
||||
id: number
|
||||
operation: string
|
||||
variant_id: string | null
|
||||
}
|
||||
Insert: {
|
||||
account_id: string
|
||||
cart_id: string
|
||||
changed_by: string
|
||||
comment?: string | null
|
||||
created_at?: string
|
||||
id?: number
|
||||
operation: string
|
||||
variant_id?: string | null
|
||||
}
|
||||
Update: {
|
||||
account_id?: string
|
||||
cart_id?: string
|
||||
changed_by?: string
|
||||
comment?: string | null
|
||||
created_at?: string
|
||||
id?: number
|
||||
operation?: string
|
||||
variant_id?: string | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
log_entries: {
|
||||
Row: {
|
||||
changed_at: string
|
||||
@@ -116,29 +149,7 @@ export type Database = {
|
||||
status?: string
|
||||
}
|
||||
Relationships: []
|
||||
},
|
||||
cart_entries: {
|
||||
Row: {
|
||||
id: number
|
||||
account_id: string
|
||||
cart_id: string
|
||||
operation: string
|
||||
variant_id: string
|
||||
comment: string | null
|
||||
created_at: string
|
||||
changed_by: string | null
|
||||
}
|
||||
Insert: {
|
||||
id: number
|
||||
account_id: string
|
||||
cart_id: string
|
||||
operation: string
|
||||
variant_id: string
|
||||
comment: string | null
|
||||
created_at: string
|
||||
changed_by: string | null
|
||||
}
|
||||
},
|
||||
}
|
||||
Views: {
|
||||
[_ in never]: never
|
||||
@@ -685,6 +696,58 @@ export type Database = {
|
||||
},
|
||||
]
|
||||
}
|
||||
company_params: {
|
||||
Row: {
|
||||
account_id: string | null
|
||||
benefit_amount: number | null
|
||||
benefit_occurance: string | null
|
||||
created_at: string | null
|
||||
id: string
|
||||
slug: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
Insert: {
|
||||
account_id?: string | null
|
||||
benefit_amount?: number | null
|
||||
benefit_occurance?: string | null
|
||||
created_at?: string | null
|
||||
id?: string
|
||||
slug?: string | null
|
||||
updated_at?: string | null
|
||||
}
|
||||
Update: {
|
||||
account_id?: string | null
|
||||
benefit_amount?: number | null
|
||||
benefit_occurance?: string | null
|
||||
created_at?: string | null
|
||||
id?: string
|
||||
slug?: string | null
|
||||
updated_at?: string | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "company_params_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "accounts"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "company_params_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "user_account_workspace"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "company_params_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "user_accounts"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
config: {
|
||||
Row: {
|
||||
billing_provider: Database["medreport"]["Enums"]["billing_provider"]
|
||||
@@ -1628,6 +1691,10 @@ export type Database = {
|
||||
Args: { target_account_id: string }
|
||||
Returns: boolean
|
||||
}
|
||||
is_company_admin: {
|
||||
Args: { account_slug: string }
|
||||
Returns: boolean
|
||||
}
|
||||
is_mfa_compliant: {
|
||||
Args: Record<PropertyKey, never>
|
||||
Returns: boolean
|
||||
|
||||
@@ -119,5 +119,24 @@
|
||||
},
|
||||
"cart": {
|
||||
"label": "Cart ({{ items }})"
|
||||
},
|
||||
"pageTitle": "{{companyName}} eelarve",
|
||||
"description": "Vali kalendrist sobiv kuupäev ja broneeri endale vastuvõtuaeg.",
|
||||
"saveChanges": "Salvesta muudatused",
|
||||
"healthBenefitForm": {
|
||||
"title": "Tervisetoetuse vorm",
|
||||
"description": "Ettevõtte Tervisekassa toetus töötajale",
|
||||
"info": "* Hindadele lisanduvad riigipoolsed maksud"
|
||||
},
|
||||
"occurance": {
|
||||
"yearly": "Kord aastas",
|
||||
"quarterly": "kord kvartalis",
|
||||
"monthly": "Kord kuus"
|
||||
},
|
||||
"expensesOverview": {
|
||||
"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 *"
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,9 @@
|
||||
"personalCode": "Isikukood",
|
||||
"city": "Linn",
|
||||
"weight": "Kaal",
|
||||
"height": "Pikkus"
|
||||
"height": "Pikkus",
|
||||
"occurance": "Toetuse sagedus",
|
||||
"amount": "Summa"
|
||||
},
|
||||
"wallet": {
|
||||
"balance": "Sinu MedReporti konto seis",
|
||||
|
||||
@@ -71,11 +71,11 @@
|
||||
}
|
||||
|
||||
h4 {
|
||||
@apply text-2xl;
|
||||
@apply text-xl;
|
||||
}
|
||||
|
||||
h5 {
|
||||
@apply text-xl;
|
||||
@apply text-base;
|
||||
}
|
||||
|
||||
h6 {
|
||||
|
||||
@@ -1,227 +1,227 @@
|
||||
-- create table "public"."connected_online_providers" (
|
||||
-- "id" bigint not null,
|
||||
-- "name" text not null,
|
||||
-- "email" text,
|
||||
-- "phone_number" text,
|
||||
-- "can_select_worker" boolean not null,
|
||||
-- "personal_code_required" boolean not null,
|
||||
-- "created_at" timestamp with time zone not null default now(),
|
||||
-- "updated_at" timestamp without time zone default now()
|
||||
-- );
|
||||
create table "public"."connected_online_providers" (
|
||||
"id" bigint not null,
|
||||
"name" text not null,
|
||||
"email" text,
|
||||
"phone_number" text,
|
||||
"can_select_worker" boolean not null,
|
||||
"personal_code_required" boolean not null,
|
||||
"created_at" timestamp with time zone not null default now(),
|
||||
"updated_at" timestamp without time zone default now()
|
||||
);
|
||||
|
||||
|
||||
-- alter table "public"."connected_online_providers" enable row level security;
|
||||
alter table "public"."connected_online_providers" enable row level security;
|
||||
|
||||
-- create table "public"."connected_online_services" (
|
||||
-- "id" bigint not null,
|
||||
-- "clinic_id" bigint not null,
|
||||
-- "sync_id" bigint not null,
|
||||
-- "name" text not null,
|
||||
-- "description" text,
|
||||
-- "price" double precision not null,
|
||||
-- "requires_payment" boolean not null,
|
||||
-- "duration" bigint not null,
|
||||
-- "neto_duration" bigint,
|
||||
-- "display" text,
|
||||
-- "price_periods" text,
|
||||
-- "online_hide_duration" bigint,
|
||||
-- "online_hide_price" bigint,
|
||||
-- "code" text not null,
|
||||
-- "has_free_codes" boolean not null,
|
||||
-- "created_at" timestamp with time zone not null default now(),
|
||||
-- "updated_at" timestamp with time zone default now()
|
||||
-- );
|
||||
create table "public"."connected_online_services" (
|
||||
"id" bigint not null,
|
||||
"clinic_id" bigint not null,
|
||||
"sync_id" bigint not null,
|
||||
"name" text not null,
|
||||
"description" text,
|
||||
"price" double precision not null,
|
||||
"requires_payment" boolean not null,
|
||||
"duration" bigint not null,
|
||||
"neto_duration" bigint,
|
||||
"display" text,
|
||||
"price_periods" text,
|
||||
"online_hide_duration" bigint,
|
||||
"online_hide_price" bigint,
|
||||
"code" text not null,
|
||||
"has_free_codes" boolean not null,
|
||||
"created_at" timestamp with time zone not null default now(),
|
||||
"updated_at" timestamp with time zone default now()
|
||||
);
|
||||
|
||||
|
||||
-- alter table "public"."connected_online_services" enable row level security;
|
||||
alter table "public"."connected_online_services" enable row level security;
|
||||
|
||||
-- CREATE UNIQUE INDEX connected_online_providers_id_key ON public.connected_online_providers USING btree (id);
|
||||
CREATE UNIQUE INDEX connected_online_providers_id_key ON public.connected_online_providers USING btree (id);
|
||||
|
||||
-- CREATE UNIQUE INDEX connected_online_providers_pkey ON public.connected_online_providers USING btree (id);
|
||||
CREATE UNIQUE INDEX connected_online_providers_pkey ON public.connected_online_providers USING btree (id);
|
||||
|
||||
-- CREATE UNIQUE INDEX connected_online_services_id_key ON public.connected_online_services USING btree (id);
|
||||
CREATE UNIQUE INDEX connected_online_services_id_key ON public.connected_online_services USING btree (id);
|
||||
|
||||
-- CREATE UNIQUE INDEX connected_online_services_pkey ON public.connected_online_services USING btree (id);
|
||||
CREATE UNIQUE INDEX connected_online_services_pkey ON public.connected_online_services USING btree (id);
|
||||
|
||||
-- alter table "public"."connected_online_providers" add constraint "connected_online_providers_pkey" PRIMARY KEY using index "connected_online_providers_pkey";
|
||||
alter table "public"."connected_online_providers" add constraint "connected_online_providers_pkey" PRIMARY KEY using index "connected_online_providers_pkey";
|
||||
|
||||
-- alter table "public"."connected_online_services" add constraint "connected_online_services_pkey" PRIMARY KEY using index "connected_online_services_pkey";
|
||||
alter table "public"."connected_online_services" add constraint "connected_online_services_pkey" PRIMARY KEY using index "connected_online_services_pkey";
|
||||
|
||||
-- alter table "public"."connected_online_providers" add constraint "connected_online_providers_id_key" UNIQUE using index "connected_online_providers_id_key";
|
||||
alter table "public"."connected_online_providers" add constraint "connected_online_providers_id_key" UNIQUE using index "connected_online_providers_id_key";
|
||||
|
||||
-- alter table "public"."connected_online_services" add constraint "connected_online_services_clinic_id_fkey" FOREIGN KEY (clinic_id) REFERENCES connected_online_providers(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
alter table "public"."connected_online_services" add constraint "connected_online_services_clinic_id_fkey" FOREIGN KEY (clinic_id) REFERENCES connected_online_providers(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
-- alter table "public"."connected_online_services" validate constraint "connected_online_services_clinic_id_fkey";
|
||||
alter table "public"."connected_online_services" validate constraint "connected_online_services_clinic_id_fkey";
|
||||
|
||||
-- alter table "public"."connected_online_services" add constraint "connected_online_services_id_key" UNIQUE using index "connected_online_services_id_key";
|
||||
alter table "public"."connected_online_services" add constraint "connected_online_services_id_key" UNIQUE using index "connected_online_services_id_key";
|
||||
|
||||
-- grant delete on table "public"."connected_online_providers" to "service_role";
|
||||
grant delete on table "public"."connected_online_providers" to "service_role";
|
||||
|
||||
-- grant insert on table "public"."connected_online_providers" to "service_role";
|
||||
grant insert on table "public"."connected_online_providers" to "service_role";
|
||||
|
||||
-- grant references on table "public"."connected_online_providers" to "service_role";
|
||||
grant references on table "public"."connected_online_providers" to "service_role";
|
||||
|
||||
-- grant select on table "public"."connected_online_providers" to "service_role";
|
||||
grant select on table "public"."connected_online_providers" to "service_role";
|
||||
|
||||
-- grant trigger on table "public"."connected_online_providers" to "service_role";
|
||||
grant trigger on table "public"."connected_online_providers" to "service_role";
|
||||
|
||||
-- grant truncate on table "public"."connected_online_providers" to "service_role";
|
||||
grant truncate on table "public"."connected_online_providers" to "service_role";
|
||||
|
||||
-- grant update on table "public"."connected_online_providers" to "service_role";
|
||||
grant update on table "public"."connected_online_providers" to "service_role";
|
||||
|
||||
-- grant select on table "public"."connected_online_providers" to "authenticated";
|
||||
grant select on table "public"."connected_online_providers" to "authenticated";
|
||||
|
||||
-- grant delete on table "public"."connected_online_services" to "service_role";
|
||||
grant delete on table "public"."connected_online_services" to "service_role";
|
||||
|
||||
-- grant insert on table "public"."connected_online_services" to "service_role";
|
||||
grant insert on table "public"."connected_online_services" to "service_role";
|
||||
|
||||
-- grant references on table "public"."connected_online_services" to "service_role";
|
||||
grant references on table "public"."connected_online_services" to "service_role";
|
||||
|
||||
-- grant select on table "public"."connected_online_services" to "service_role";
|
||||
grant select on table "public"."connected_online_services" to "service_role";
|
||||
|
||||
-- grant trigger on table "public"."connected_online_services" to "service_role";
|
||||
grant trigger on table "public"."connected_online_services" to "service_role";
|
||||
|
||||
-- grant truncate on table "public"."connected_online_services" to "service_role";
|
||||
grant truncate on table "public"."connected_online_services" to "service_role";
|
||||
|
||||
-- grant update on table "public"."connected_online_services" to "service_role";
|
||||
grant update on table "public"."connected_online_services" to "service_role";
|
||||
|
||||
-- grant select on table "public"."connected_online_services" to "authenticated";
|
||||
grant select on table "public"."connected_online_services" to "authenticated";
|
||||
|
||||
-- create type "audit"."request_status" as enum ('SUCCESS', 'FAIL');
|
||||
create type "audit"."request_status" as enum ('SUCCESS', 'FAIL');
|
||||
|
||||
-- create table "audit"."request_entries" (
|
||||
-- "id" bigint generated by default as identity not null,
|
||||
-- "personal_code" bigint,
|
||||
-- "request_api" text not null,
|
||||
-- "request_api_method" text not null,
|
||||
-- "status" audit.request_status not null,
|
||||
-- "comment" text,
|
||||
-- "service_provider_id" bigint,
|
||||
-- "service_id" bigint,
|
||||
-- "requested_start_date" timestamp with time zone,
|
||||
-- "requested_end_date" timestamp with time zone,
|
||||
-- "created_at" timestamp with time zone not null default now()
|
||||
-- );
|
||||
create table "audit"."request_entries" (
|
||||
"id" bigint generated by default as identity not null,
|
||||
"personal_code" bigint,
|
||||
"request_api" text not null,
|
||||
"request_api_method" text not null,
|
||||
"status" audit.request_status not null,
|
||||
"comment" text,
|
||||
"service_provider_id" bigint,
|
||||
"service_id" bigint,
|
||||
"requested_start_date" timestamp with time zone,
|
||||
"requested_end_date" timestamp with time zone,
|
||||
"created_at" timestamp with time zone not null default now()
|
||||
);
|
||||
|
||||
|
||||
-- alter table "audit"."request_entries" enable row level security;
|
||||
alter table "audit"."request_entries" enable row level security;
|
||||
|
||||
-- CREATE UNIQUE INDEX request_entries_pkey ON audit.request_entries USING btree (id);
|
||||
CREATE UNIQUE INDEX request_entries_pkey ON audit.request_entries USING btree (id);
|
||||
|
||||
-- alter table "audit"."request_entries" add constraint "request_entries_pkey" PRIMARY KEY using index "request_entries_pkey";
|
||||
alter table "audit"."request_entries" add constraint "request_entries_pkey" PRIMARY KEY using index "request_entries_pkey";
|
||||
|
||||
-- grant delete on table "audit"."request_entries" to "service_role";
|
||||
grant delete on table "audit"."request_entries" to "service_role";
|
||||
|
||||
-- grant insert on table "audit"."request_entries" to "service_role";
|
||||
grant insert on table "audit"."request_entries" to "service_role";
|
||||
|
||||
-- grant references on table "audit"."request_entries" to "service_role";
|
||||
grant references on table "audit"."request_entries" to "service_role";
|
||||
|
||||
-- grant select on table "audit"."request_entries" to "service_role";
|
||||
grant select on table "audit"."request_entries" to "service_role";
|
||||
|
||||
-- grant trigger on table "audit"."request_entries" to "service_role";
|
||||
grant trigger on table "audit"."request_entries" to "service_role";
|
||||
|
||||
-- grant truncate on table "audit"."request_entries" to "service_role";
|
||||
grant truncate on table "audit"."request_entries" to "service_role";
|
||||
|
||||
-- grant update on table "audit"."request_entries" to "service_role";
|
||||
grant update on table "audit"."request_entries" to "service_role";
|
||||
|
||||
-- create policy "service_role_all"
|
||||
-- on "audit"."request_entries"
|
||||
-- as permissive
|
||||
-- for all
|
||||
-- to service_role
|
||||
-- using (true);
|
||||
create policy "service_role_all"
|
||||
on "audit"."request_entries"
|
||||
as permissive
|
||||
for all
|
||||
to service_role
|
||||
using (true);
|
||||
|
||||
-- create table "public"."connected_online_reservation" (
|
||||
-- "id" bigint generated by default as identity not null,
|
||||
-- "user_id" uuid not null,
|
||||
-- "booking_code" text not null,
|
||||
-- "service_id" bigint not null,
|
||||
-- "clinic_id" bigint not null,
|
||||
-- "service_user_id" bigint,
|
||||
-- "sync_user_id" bigint not null,
|
||||
-- "requires_payment" boolean not null,
|
||||
-- "comments" text,
|
||||
-- "start_time" timestamp with time zone not null,
|
||||
-- "lang" text not null,
|
||||
-- "discount_code" text,
|
||||
-- "created_at" timestamp with time zone not null default now(),
|
||||
-- "updated_at" timestamp with time zone default now()
|
||||
-- );
|
||||
create table "public"."connected_online_reservation" (
|
||||
"id" bigint generated by default as identity not null,
|
||||
"user_id" uuid not null,
|
||||
"booking_code" text not null,
|
||||
"service_id" bigint not null,
|
||||
"clinic_id" bigint not null,
|
||||
"service_user_id" bigint,
|
||||
"sync_user_id" bigint not null,
|
||||
"requires_payment" boolean not null,
|
||||
"comments" text,
|
||||
"start_time" timestamp with time zone not null,
|
||||
"lang" text not null,
|
||||
"discount_code" text,
|
||||
"created_at" timestamp with time zone not null default now(),
|
||||
"updated_at" timestamp with time zone default now()
|
||||
);
|
||||
|
||||
|
||||
-- alter table "public"."connected_online_reservation" enable row level security;
|
||||
alter table "public"."connected_online_reservation" enable row level security;
|
||||
|
||||
-- CREATE UNIQUE INDEX connected_online_reservation_booking_code_key ON public.connected_online_reservation USING btree (booking_code);
|
||||
CREATE UNIQUE INDEX connected_online_reservation_booking_code_key ON public.connected_online_reservation USING btree (booking_code);
|
||||
|
||||
-- CREATE UNIQUE INDEX connected_online_reservation_pkey ON public.connected_online_reservation USING btree (id);
|
||||
CREATE UNIQUE INDEX connected_online_reservation_pkey ON public.connected_online_reservation USING btree (id);
|
||||
|
||||
-- alter table "public"."connected_online_reservation" add constraint "connected_online_reservation_pkey" PRIMARY KEY using index "connected_online_reservation_pkey";
|
||||
alter table "public"."connected_online_reservation" add constraint "connected_online_reservation_pkey" PRIMARY KEY using index "connected_online_reservation_pkey";
|
||||
|
||||
-- alter table "public"."connected_online_reservation" add constraint "connected_online_reservation_booking_code_key" UNIQUE using index "connected_online_reservation_booking_code_key";
|
||||
alter table "public"."connected_online_reservation" add constraint "connected_online_reservation_booking_code_key" UNIQUE using index "connected_online_reservation_booking_code_key";
|
||||
|
||||
-- alter table "public"."connected_online_reservation" add constraint "connected_online_reservation_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
alter table "public"."connected_online_reservation" add constraint "connected_online_reservation_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
-- alter table "public"."connected_online_reservation" validate constraint "connected_online_reservation_user_id_fkey";
|
||||
alter table "public"."connected_online_reservation" validate constraint "connected_online_reservation_user_id_fkey";
|
||||
|
||||
-- grant delete on table "public"."connected_online_reservation" to "service_role";
|
||||
grant delete on table "public"."connected_online_reservation" to "service_role";
|
||||
|
||||
-- grant insert on table "public"."connected_online_reservation" to "service_role";
|
||||
grant insert on table "public"."connected_online_reservation" to "service_role";
|
||||
|
||||
-- grant references on table "public"."connected_online_reservation" to "service_role";
|
||||
grant references on table "public"."connected_online_reservation" to "service_role";
|
||||
|
||||
-- grant select on table "public"."connected_online_reservation" to "service_role";
|
||||
grant select on table "public"."connected_online_reservation" to "service_role";
|
||||
|
||||
-- grant trigger on table "public"."connected_online_reservation" to "service_role";
|
||||
grant trigger on table "public"."connected_online_reservation" to "service_role";
|
||||
|
||||
-- grant truncate on table "public"."connected_online_reservation" to "service_role";
|
||||
grant truncate on table "public"."connected_online_reservation" to "service_role";
|
||||
|
||||
-- grant update on table "public"."connected_online_reservation" to "service_role";
|
||||
grant update on table "public"."connected_online_reservation" to "service_role";
|
||||
|
||||
-- create policy "service_role_all"
|
||||
-- on "public"."connected_online_reservation"
|
||||
-- as permissive
|
||||
-- for all
|
||||
-- to service_role
|
||||
-- using (true);
|
||||
create policy "service_role_all"
|
||||
on "public"."connected_online_reservation"
|
||||
as permissive
|
||||
for all
|
||||
to service_role
|
||||
using (true);
|
||||
|
||||
|
||||
-- CREATE TRIGGER connected_online_providers_change_record_timestamps AFTER INSERT OR UPDATE ON public.connected_online_providers FOR EACH ROW EXECUTE FUNCTION trigger_set_timestamps();
|
||||
CREATE TRIGGER connected_online_providers_change_record_timestamps AFTER INSERT OR UPDATE ON public.connected_online_providers FOR EACH ROW EXECUTE FUNCTION trigger_set_timestamps();
|
||||
|
||||
-- CREATE TRIGGER connected_online_services_change_record_timestamps AFTER INSERT OR UPDATE ON public.connected_online_services FOR EACH ROW EXECUTE FUNCTION trigger_set_timestamps();
|
||||
CREATE TRIGGER connected_online_services_change_record_timestamps AFTER INSERT OR UPDATE ON public.connected_online_services FOR EACH ROW EXECUTE FUNCTION trigger_set_timestamps();
|
||||
|
||||
-- create policy "service_role_all"
|
||||
-- on "public"."connected_online_providers"
|
||||
-- as permissive
|
||||
-- for all
|
||||
-- to service_role
|
||||
-- using (true);
|
||||
create policy "service_role_all"
|
||||
on "public"."connected_online_providers"
|
||||
as permissive
|
||||
for all
|
||||
to service_role
|
||||
using (true);
|
||||
|
||||
|
||||
-- create policy "service_role_all"
|
||||
-- on "public"."connected_online_services"
|
||||
-- as permissive
|
||||
-- for all
|
||||
-- to service_role
|
||||
-- using (true);
|
||||
create policy "service_role_all"
|
||||
on "public"."connected_online_services"
|
||||
as permissive
|
||||
for all
|
||||
to service_role
|
||||
using (true);
|
||||
|
||||
-- create policy "authenticated_select"
|
||||
-- on "public"."connected_online_providers"
|
||||
-- as permissive
|
||||
-- for select
|
||||
-- to authenticated
|
||||
-- using (true);
|
||||
create policy "authenticated_select"
|
||||
on "public"."connected_online_providers"
|
||||
as permissive
|
||||
for select
|
||||
to authenticated
|
||||
using (true);
|
||||
|
||||
-- create policy "authenticated_select"
|
||||
-- on "public"."connected_online_services"
|
||||
-- as permissive
|
||||
-- for select
|
||||
-- to authenticated
|
||||
-- using (true);
|
||||
create policy "authenticated_select"
|
||||
on "public"."connected_online_services"
|
||||
as permissive
|
||||
for select
|
||||
to authenticated
|
||||
using (true);
|
||||
|
||||
|
||||
-- create policy "own_all"
|
||||
-- on "public"."connected_online_reservation"
|
||||
-- as permissive
|
||||
-- for all
|
||||
-- to authenticated
|
||||
-- using ((( SELECT auth.uid() AS uid) = user_id));
|
||||
create policy "own_all"
|
||||
on "public"."connected_online_reservation"
|
||||
as permissive
|
||||
for all
|
||||
to authenticated
|
||||
using ((( SELECT auth.uid() AS uid) = user_id));
|
||||
@@ -1,225 +1,225 @@
|
||||
|
||||
-- create table "public"."medreport_product_groups" (
|
||||
-- "id" bigint generated by default as identity not null,
|
||||
-- "name" text not null,
|
||||
-- "created_at" timestamp with time zone not null default now(),
|
||||
-- "updated_at" timestamp with time zone
|
||||
-- );
|
||||
create table "public"."medreport_product_groups" (
|
||||
"id" bigint generated by default as identity not null,
|
||||
"name" text not null,
|
||||
"created_at" timestamp with time zone not null default now(),
|
||||
"updated_at" timestamp with time zone
|
||||
);
|
||||
|
||||
-- create table "public"."medreport_products" (
|
||||
-- "id" bigint generated by default as identity not null,
|
||||
-- "name" text not null,
|
||||
-- "product_group_id" bigint,
|
||||
-- "created_at" timestamp with time zone not null default now(),
|
||||
-- "updated_at" timestamp with time zone default now()
|
||||
-- );
|
||||
create table "public"."medreport_products" (
|
||||
"id" bigint generated by default as identity not null,
|
||||
"name" text not null,
|
||||
"product_group_id" bigint,
|
||||
"created_at" timestamp with time zone not null default now(),
|
||||
"updated_at" timestamp with time zone default now()
|
||||
);
|
||||
|
||||
|
||||
-- alter table "public"."medreport_products" enable row level security;
|
||||
alter table "public"."medreport_products" enable row level security;
|
||||
|
||||
-- create table "public"."medreport_products_analyses_relations" (
|
||||
-- "product_id" bigint not null,
|
||||
-- "analysis_element_id" bigint,
|
||||
-- "analysis_id" bigint
|
||||
-- );
|
||||
create table "public"."medreport_products_analyses_relations" (
|
||||
"product_id" bigint not null,
|
||||
"analysis_element_id" bigint,
|
||||
"analysis_id" bigint
|
||||
);
|
||||
|
||||
-- alter table "public"."medreport_product_groups" enable row level security;
|
||||
alter table "public"."medreport_product_groups" enable row level security;
|
||||
|
||||
-- alter table "public"."medreport_products_analyses_relations" enable row level security;
|
||||
alter table "public"."medreport_products_analyses_relations" enable row level security;
|
||||
|
||||
-- CREATE UNIQUE INDEX medreport_product_groups_name_key ON public.medreport_product_groups USING btree (name);
|
||||
CREATE UNIQUE INDEX medreport_product_groups_name_key ON public.medreport_product_groups USING btree (name);
|
||||
|
||||
-- CREATE UNIQUE INDEX medreport_product_groups_pkey ON public.medreport_product_groups USING btree (id);
|
||||
CREATE UNIQUE INDEX medreport_product_groups_pkey ON public.medreport_product_groups USING btree (id);
|
||||
|
||||
-- alter table "public"."medreport_product_groups" add constraint "medreport_product_groups_pkey" PRIMARY KEY using index "medreport_product_groups_pkey";
|
||||
alter table "public"."medreport_product_groups" add constraint "medreport_product_groups_pkey" PRIMARY KEY using index "medreport_product_groups_pkey";
|
||||
|
||||
-- alter table "public"."medreport_product_groups" add constraint "medreport_product_groups_name_key" UNIQUE using index "medreport_product_groups_name_key";
|
||||
alter table "public"."medreport_product_groups" add constraint "medreport_product_groups_name_key" UNIQUE using index "medreport_product_groups_name_key";
|
||||
|
||||
-- alter table "public"."medreport_products" add constraint "medreport_products_product_groups_id_fkey" FOREIGN KEY (product_group_id) REFERENCES medreport_product_groups(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
alter table "public"."medreport_products" add constraint "medreport_products_product_groups_id_fkey" FOREIGN KEY (product_group_id) REFERENCES medreport_product_groups(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
-- alter table "public"."medreport_products" validate constraint "medreport_products_product_groups_id_fkey";
|
||||
alter table "public"."medreport_products" validate constraint "medreport_products_product_groups_id_fkey";
|
||||
|
||||
-- grant select on table "public"."medreport_product_groups" to "anon";
|
||||
grant select on table "public"."medreport_product_groups" to "anon";
|
||||
|
||||
-- grant select on table "public"."medreport_product_groups" to "authenticated";
|
||||
grant select on table "public"."medreport_product_groups" to "authenticated";
|
||||
|
||||
-- grant delete on table "public"."medreport_product_groups" to "service_role";
|
||||
grant delete on table "public"."medreport_product_groups" to "service_role";
|
||||
|
||||
-- grant insert on table "public"."medreport_product_groups" to "service_role";
|
||||
grant insert on table "public"."medreport_product_groups" to "service_role";
|
||||
|
||||
-- grant references on table "public"."medreport_product_groups" to "service_role";
|
||||
grant references on table "public"."medreport_product_groups" to "service_role";
|
||||
|
||||
-- grant select on table "public"."medreport_product_groups" to "service_role";
|
||||
grant select on table "public"."medreport_product_groups" to "service_role";
|
||||
|
||||
-- grant trigger on table "public"."medreport_product_groups" to "service_role";
|
||||
grant trigger on table "public"."medreport_product_groups" to "service_role";
|
||||
|
||||
-- grant truncate on table "public"."medreport_product_groups" to "service_role";
|
||||
grant truncate on table "public"."medreport_product_groups" to "service_role";
|
||||
|
||||
-- grant update on table "public"."medreport_product_groups" to "service_role";
|
||||
grant update on table "public"."medreport_product_groups" to "service_role";
|
||||
|
||||
-- CREATE UNIQUE INDEX medreport_products_analyses_analysis_element_id_key ON public.medreport_products_analyses_relations USING btree (analysis_element_id);
|
||||
CREATE UNIQUE INDEX medreport_products_analyses_analysis_element_id_key ON public.medreport_products_analyses_relations USING btree (analysis_element_id);
|
||||
|
||||
-- CREATE UNIQUE INDEX medreport_products_analyses_analysis_id_key ON public.medreport_products_analyses_relations USING btree (analysis_id);
|
||||
CREATE UNIQUE INDEX medreport_products_analyses_analysis_id_key ON public.medreport_products_analyses_relations USING btree (analysis_id);
|
||||
|
||||
-- CREATE UNIQUE INDEX medreport_products_analyses_pkey ON public.medreport_products_analyses_relations USING btree (product_id);
|
||||
CREATE UNIQUE INDEX medreport_products_analyses_pkey ON public.medreport_products_analyses_relations USING btree (product_id);
|
||||
|
||||
-- CREATE UNIQUE INDEX medreport_products_name_key ON public.medreport_products USING btree (name);
|
||||
CREATE UNIQUE INDEX medreport_products_name_key ON public.medreport_products USING btree (name);
|
||||
|
||||
-- CREATE UNIQUE INDEX medreport_products_pkey ON public.medreport_products USING btree (id);
|
||||
CREATE UNIQUE INDEX medreport_products_pkey ON public.medreport_products USING btree (id);
|
||||
|
||||
-- alter table "public"."medreport_products" add constraint "medreport_products_pkey" PRIMARY KEY using index "medreport_products_pkey";
|
||||
alter table "public"."medreport_products" add constraint "medreport_products_pkey" PRIMARY KEY using index "medreport_products_pkey";
|
||||
|
||||
-- alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_pkey" PRIMARY KEY using index "medreport_products_analyses_pkey";
|
||||
alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_pkey" PRIMARY KEY using index "medreport_products_analyses_pkey";
|
||||
|
||||
-- alter table "public"."medreport_products" add constraint "medreport_products_name_key" UNIQUE using index "medreport_products_name_key";
|
||||
alter table "public"."medreport_products" add constraint "medreport_products_name_key" UNIQUE using index "medreport_products_name_key";
|
||||
|
||||
-- alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_analysis_element_id_fkey" FOREIGN KEY (analysis_element_id) REFERENCES analysis_elements(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_analysis_element_id_fkey" FOREIGN KEY (analysis_element_id) REFERENCES analysis_elements(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
-- alter table "public"."medreport_products_analyses_relations" validate constraint "medreport_products_analyses_analysis_element_id_fkey";
|
||||
alter table "public"."medreport_products_analyses_relations" validate constraint "medreport_products_analyses_analysis_element_id_fkey";
|
||||
|
||||
-- alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_analysis_element_id_key" UNIQUE using index "medreport_products_analyses_analysis_element_id_key";
|
||||
alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_analysis_element_id_key" UNIQUE using index "medreport_products_analyses_analysis_element_id_key";
|
||||
|
||||
-- alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_analysis_id_fkey" FOREIGN KEY (analysis_id) REFERENCES analyses(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_analysis_id_fkey" FOREIGN KEY (analysis_id) REFERENCES analyses(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
-- alter table "public"."medreport_products_analyses_relations" validate constraint "medreport_products_analyses_analysis_id_fkey";
|
||||
alter table "public"."medreport_products_analyses_relations" validate constraint "medreport_products_analyses_analysis_id_fkey";
|
||||
|
||||
-- alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_analysis_id_key" UNIQUE using index "medreport_products_analyses_analysis_id_key";
|
||||
alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_analysis_id_key" UNIQUE using index "medreport_products_analyses_analysis_id_key";
|
||||
|
||||
-- alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_product_id_fkey" FOREIGN KEY (product_id) REFERENCES medreport_products(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_product_id_fkey" FOREIGN KEY (product_id) REFERENCES medreport_products(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
-- alter table "public"."medreport_products_analyses_relations" validate constraint "medreport_products_analyses_product_id_fkey";
|
||||
alter table "public"."medreport_products_analyses_relations" validate constraint "medreport_products_analyses_product_id_fkey";
|
||||
|
||||
-- alter table "public"."medreport_products_analyses_relations" add constraint "product_can_be_tied_to_only_one_external_item" CHECK (((analysis_id IS NULL) OR (analysis_element_id IS NULL))) not valid;
|
||||
alter table "public"."medreport_products_analyses_relations" add constraint "product_can_be_tied_to_only_one_external_item" CHECK (((analysis_id IS NULL) OR (analysis_element_id IS NULL))) not valid;
|
||||
|
||||
-- alter table "public"."medreport_products_analyses_relations" validate constraint "product_can_be_tied_to_only_one_external_item";
|
||||
alter table "public"."medreport_products_analyses_relations" validate constraint "product_can_be_tied_to_only_one_external_item";
|
||||
|
||||
-- grant select on table "public"."medreport_products" to "anon";
|
||||
grant select on table "public"."medreport_products" to "anon";
|
||||
|
||||
-- grant select on table "public"."medreport_products" to "authenticated";
|
||||
grant select on table "public"."medreport_products" to "authenticated";
|
||||
|
||||
-- grant delete on table "public"."medreport_products" to "service_role";
|
||||
grant delete on table "public"."medreport_products" to "service_role";
|
||||
|
||||
-- grant insert on table "public"."medreport_products" to "service_role";
|
||||
grant insert on table "public"."medreport_products" to "service_role";
|
||||
|
||||
-- grant references on table "public"."medreport_products" to "service_role";
|
||||
grant references on table "public"."medreport_products" to "service_role";
|
||||
|
||||
-- grant select on table "public"."medreport_products" to "service_role";
|
||||
grant select on table "public"."medreport_products" to "service_role";
|
||||
|
||||
-- grant trigger on table "public"."medreport_products" to "service_role";
|
||||
grant trigger on table "public"."medreport_products" to "service_role";
|
||||
|
||||
-- grant truncate on table "public"."medreport_products" to "service_role";
|
||||
grant truncate on table "public"."medreport_products" to "service_role";
|
||||
|
||||
-- grant update on table "public"."medreport_products" to "service_role";
|
||||
grant update on table "public"."medreport_products" to "service_role";
|
||||
|
||||
-- grant select on table "public"."medreport_products_analyses_relations" to "anon";
|
||||
grant select on table "public"."medreport_products_analyses_relations" to "anon";
|
||||
|
||||
-- grant select on table "public"."medreport_products_analyses_relations" to "authenticated";
|
||||
grant select on table "public"."medreport_products_analyses_relations" to "authenticated";
|
||||
|
||||
-- grant delete on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
grant delete on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
|
||||
-- grant insert on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
grant insert on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
|
||||
-- grant references on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
grant references on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
|
||||
-- grant select on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
grant select on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
|
||||
-- grant trigger on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
grant trigger on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
|
||||
-- grant truncate on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
grant truncate on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
|
||||
-- grant update on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
grant update on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
|
||||
-- create policy "Enable read access for all users"
|
||||
-- on "public"."medreport_products_analyses_relations"
|
||||
-- as permissive
|
||||
-- for select
|
||||
-- to public
|
||||
-- using (true);
|
||||
create policy "Enable read access for all users"
|
||||
on "public"."medreport_products_analyses_relations"
|
||||
as permissive
|
||||
for select
|
||||
to public
|
||||
using (true);
|
||||
|
||||
|
||||
-- ALTER TABLE medreport_products_analyses_relations
|
||||
-- ADD CONSTRAINT product_can_be_tied_to_only_one_analysis_item
|
||||
-- CHECK (analysis_id IS NULL OR analysis_element_id IS NULL);
|
||||
ALTER TABLE medreport_products_analyses_relations
|
||||
ADD CONSTRAINT product_can_be_tied_to_only_one_analysis_item
|
||||
CHECK (analysis_id IS NULL OR analysis_element_id IS NULL);
|
||||
|
||||
|
||||
-- create table "public"."medreport_products_external_services_relations" (
|
||||
-- "product_id" bigint not null,
|
||||
-- "connected_online_service_id" bigint not null
|
||||
-- );
|
||||
create table "public"."medreport_products_external_services_relations" (
|
||||
"product_id" bigint not null,
|
||||
"connected_online_service_id" bigint not null
|
||||
);
|
||||
|
||||
-- alter table "public"."medreport_products_external_services_relations" enable row level security;
|
||||
alter table "public"."medreport_products_external_services_relations" enable row level security;
|
||||
|
||||
-- CREATE UNIQUE INDEX medreport_products_connected_online_services_id_key ON public.medreport_products_external_services_relations USING btree (connected_online_service_id);
|
||||
CREATE UNIQUE INDEX medreport_products_connected_online_services_id_key ON public.medreport_products_external_services_relations USING btree (connected_online_service_id);
|
||||
|
||||
-- CREATE UNIQUE INDEX medreport_products_connected_online_services_pkey ON public.medreport_products_external_services_relations USING btree (connected_online_service_id);
|
||||
CREATE UNIQUE INDEX medreport_products_connected_online_services_pkey ON public.medreport_products_external_services_relations USING btree (connected_online_service_id);
|
||||
|
||||
-- alter table "public"."medreport_products_external_services_relations" add constraint "medreport_products_connected_online_services_pkey" PRIMARY KEY using index "medreport_products_connected_online_services_pkey";
|
||||
alter table "public"."medreport_products_external_services_relations" add constraint "medreport_products_connected_online_services_pkey" PRIMARY KEY using index "medreport_products_connected_online_services_pkey";
|
||||
|
||||
-- alter table "public"."medreport_products_external_services_relations" add constraint "medreport_products_connected_online_services_id_fkey" FOREIGN KEY (connected_online_service_id) REFERENCES connected_online_services(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
alter table "public"."medreport_products_external_services_relations" add constraint "medreport_products_connected_online_services_id_fkey" FOREIGN KEY (connected_online_service_id) REFERENCES connected_online_services(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
-- alter table "public"."medreport_products_external_services_relations" validate constraint "medreport_products_connected_online_services_id_fkey";
|
||||
alter table "public"."medreport_products_external_services_relations" validate constraint "medreport_products_connected_online_services_id_fkey";
|
||||
|
||||
-- alter table "public"."medreport_products_external_services_relations" add constraint "medreport_products_connected_online_services_id_key" UNIQUE using index "medreport_products_connected_online_services_id_key";
|
||||
alter table "public"."medreport_products_external_services_relations" add constraint "medreport_products_connected_online_services_id_key" UNIQUE using index "medreport_products_connected_online_services_id_key";
|
||||
|
||||
-- alter table "public"."medreport_products_external_services_relations" add constraint "medreport_products_connected_online_services_product_id_fkey" FOREIGN KEY (product_id) REFERENCES medreport_products(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
alter table "public"."medreport_products_external_services_relations" add constraint "medreport_products_connected_online_services_product_id_fkey" FOREIGN KEY (product_id) REFERENCES medreport_products(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
-- alter table "public"."medreport_products_external_services_relations" validate constraint "medreport_products_connected_online_services_product_id_fkey";
|
||||
alter table "public"."medreport_products_external_services_relations" validate constraint "medreport_products_connected_online_services_product_id_fkey";
|
||||
|
||||
-- grant select on table "public"."medreport_products_external_services_relations" to "anon";
|
||||
grant select on table "public"."medreport_products_external_services_relations" to "anon";
|
||||
|
||||
-- grant select on table "public"."medreport_products_external_services_relations" to "authenticated";
|
||||
grant select on table "public"."medreport_products_external_services_relations" to "authenticated";
|
||||
|
||||
-- grant delete on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
grant delete on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
|
||||
-- grant insert on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
grant insert on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
|
||||
-- grant references on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
grant references on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
|
||||
-- grant select on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
grant select on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
|
||||
-- grant trigger on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
grant trigger on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
|
||||
-- grant truncate on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
grant truncate on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
|
||||
-- grant update on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
grant update on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
|
||||
-- CREATE OR REPLACE FUNCTION check_tied_to_connected_online()
|
||||
-- RETURNS TRIGGER AS $$
|
||||
-- BEGIN
|
||||
-- IF EXISTS (
|
||||
-- SELECT 1
|
||||
-- FROM medreport_products_external_services_relations
|
||||
-- WHERE product_id = NEW.product_id
|
||||
-- ) THEN
|
||||
-- RAISE EXCEPTION 'Value "%" already exists in medreport_products_external_services_relations', NEW.product_id;
|
||||
-- END IF;
|
||||
CREATE OR REPLACE FUNCTION check_tied_to_connected_online()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM medreport_products_external_services_relations
|
||||
WHERE product_id = NEW.product_id
|
||||
) THEN
|
||||
RAISE EXCEPTION 'Value "%" already exists in medreport_products_external_services_relations', NEW.product_id;
|
||||
END IF;
|
||||
|
||||
-- RETURN NEW;
|
||||
-- END;
|
||||
-- $$ LANGUAGE plpgsql;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- CREATE OR REPLACE FUNCTION check_tied_to_analysis_item()
|
||||
-- RETURNS TRIGGER AS $$
|
||||
-- BEGIN
|
||||
-- IF EXISTS (
|
||||
-- SELECT 1
|
||||
-- FROM medreport_products_analyses_relations
|
||||
-- WHERE product_id = NEW.product_id
|
||||
-- ) THEN
|
||||
-- RAISE EXCEPTION 'Value "%" already exists in medreport_products_analyses_relations', NEW.product_id;
|
||||
-- END IF;
|
||||
CREATE OR REPLACE FUNCTION check_tied_to_analysis_item()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM medreport_products_analyses_relations
|
||||
WHERE product_id = NEW.product_id
|
||||
) THEN
|
||||
RAISE EXCEPTION 'Value "%" already exists in medreport_products_analyses_relations', NEW.product_id;
|
||||
END IF;
|
||||
|
||||
-- RETURN NEW;
|
||||
-- END;
|
||||
-- $$ LANGUAGE plpgsql;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- CREATE TRIGGER check_not_already_tied_to_connected_online BEFORE INSERT OR UPDATE ON public.medreport_products_analyses_relations FOR EACH ROW EXECUTE FUNCTION check_tied_to_connected_online();
|
||||
CREATE TRIGGER check_not_already_tied_to_connected_online BEFORE INSERT OR UPDATE ON public.medreport_products_analyses_relations FOR EACH ROW EXECUTE FUNCTION check_tied_to_connected_online();
|
||||
|
||||
-- CREATE TRIGGER check_not_already_tied_to_analysis BEFORE INSERT OR UPDATE ON public.medreport_products_external_services_relations FOR EACH ROW EXECUTE FUNCTION check_tied_to_analysis_item();
|
||||
CREATE TRIGGER check_not_already_tied_to_analysis BEFORE INSERT OR UPDATE ON public.medreport_products_external_services_relations FOR EACH ROW EXECUTE FUNCTION check_tied_to_analysis_item();
|
||||
|
||||
-- create policy "read_all"
|
||||
-- on "public"."medreport_product_groups"
|
||||
-- as permissive
|
||||
-- for select
|
||||
-- to public
|
||||
-- using (true);
|
||||
create policy "read_all"
|
||||
on "public"."medreport_product_groups"
|
||||
as permissive
|
||||
for select
|
||||
to public
|
||||
using (true);
|
||||
|
||||
@@ -680,17 +680,17 @@ drop policy "accounts_self_update" on "public"."accounts";
|
||||
|
||||
drop policy "create_org_account" on "public"."accounts";
|
||||
|
||||
-- drop policy "restrict_mfa_accounts" on "public"."accounts";
|
||||
drop policy "restrict_mfa_accounts" on "public"."accounts";
|
||||
|
||||
-- drop policy "super_admins_access_accounts" on "public"."accounts";
|
||||
drop policy "super_admins_access_accounts" on "public"."accounts";
|
||||
|
||||
drop policy "accounts_memberships_delete" on "public"."accounts_memberships";
|
||||
|
||||
drop policy "accounts_memberships_read" on "public"."accounts_memberships";
|
||||
|
||||
-- drop policy "restrict_mfa_accounts_memberships" on "public"."accounts_memberships";
|
||||
drop policy "restrict_mfa_accounts_memberships" on "public"."accounts_memberships";
|
||||
|
||||
-- drop policy "super_admins_access_accounts_memberships" on "public"."accounts_memberships";
|
||||
drop policy "super_admins_access_accounts_memberships" on "public"."accounts_memberships";
|
||||
|
||||
drop policy "analysis_all" on "public"."analyses";
|
||||
|
||||
@@ -742,53 +742,53 @@ drop policy "invitations_read_self" on "public"."invitations";
|
||||
|
||||
drop policy "invitations_update" on "public"."invitations";
|
||||
|
||||
-- drop policy "restrict_mfa_invitations" on "public"."invitations";
|
||||
drop policy "restrict_mfa_invitations" on "public"."invitations";
|
||||
|
||||
-- drop policy "super_admins_access_invitations" on "public"."invitations";
|
||||
drop policy "super_admins_access_invitations" on "public"."invitations";
|
||||
|
||||
drop policy "read_all" on "public"."medreport_product_groups";
|
||||
|
||||
drop policy "Enable read access for all users" on "public"."medreport_products_analyses_relations";
|
||||
|
||||
-- drop policy "Users can read their own nonces" on "public"."nonces";
|
||||
drop policy "Users can read their own nonces" on "public"."nonces";
|
||||
|
||||
drop policy "notifications_read_self" on "public"."notifications";
|
||||
|
||||
drop policy "notifications_update_self" on "public"."notifications";
|
||||
|
||||
-- drop policy "restrict_mfa_notifications" on "public"."notifications";
|
||||
drop policy "restrict_mfa_notifications" on "public"."notifications";
|
||||
|
||||
drop policy "order_items_read_self" on "public"."order_items";
|
||||
|
||||
-- drop policy "restrict_mfa_order_items" on "public"."order_items";
|
||||
drop policy "restrict_mfa_order_items" on "public"."order_items";
|
||||
|
||||
-- drop policy "super_admins_access_order_items" on "public"."order_items";
|
||||
drop policy "super_admins_access_order_items" on "public"."order_items";
|
||||
|
||||
drop policy "orders_read_self" on "public"."orders";
|
||||
|
||||
-- drop policy "restrict_mfa_orders" on "public"."orders";
|
||||
drop policy "restrict_mfa_orders" on "public"."orders";
|
||||
|
||||
-- drop policy "super_admins_access_orders" on "public"."orders";
|
||||
drop policy "super_admins_access_orders" on "public"."orders";
|
||||
|
||||
-- drop policy "restrict_mfa_role_permissions" on "public"."role_permissions";
|
||||
drop policy "restrict_mfa_role_permissions" on "public"."role_permissions";
|
||||
|
||||
drop policy "role_permissions_read" on "public"."role_permissions";
|
||||
|
||||
-- drop policy "super_admins_access_role_permissions" on "public"."role_permissions";
|
||||
drop policy "super_admins_access_role_permissions" on "public"."role_permissions";
|
||||
|
||||
drop policy "roles_read" on "public"."roles";
|
||||
|
||||
-- drop policy "restrict_mfa_subscription_items" on "public"."subscription_items";
|
||||
drop policy "restrict_mfa_subscription_items" on "public"."subscription_items";
|
||||
|
||||
drop policy "subscription_items_read_self" on "public"."subscription_items";
|
||||
|
||||
-- drop policy "super_admins_access_subscription_items" on "public"."subscription_items";
|
||||
drop policy "super_admins_access_subscription_items" on "public"."subscription_items";
|
||||
|
||||
-- drop policy "restrict_mfa_subscriptions" on "public"."subscriptions";
|
||||
drop policy "restrict_mfa_subscriptions" on "public"."subscriptions";
|
||||
|
||||
drop policy "subscriptions_read_self" on "public"."subscriptions";
|
||||
|
||||
-- drop policy "super_admins_access_subscriptions" on "public"."subscriptions";
|
||||
drop policy "super_admins_access_subscriptions" on "public"."subscriptions";
|
||||
|
||||
alter table "public"."accounts" drop constraint "accounts_created_by_fkey";
|
||||
|
||||
@@ -888,7 +888,7 @@ alter table "public"."medreport_products_analyses_relations" drop constraint "pr
|
||||
|
||||
alter table "public"."medreport_products_analyses_relations" drop constraint "product_can_be_tied_to_only_one_external_item";
|
||||
|
||||
-- alter table "public"."nonces" drop constraint "nonces_user_id_fkey";
|
||||
alter table "public"."nonces" drop constraint "nonces_user_id_fkey";
|
||||
|
||||
alter table "public"."notifications" drop constraint "notifications_account_id_fkey";
|
||||
|
||||
@@ -956,7 +956,7 @@ alter table "public"."medreport_products_analyses_relations" drop constraint "me
|
||||
|
||||
alter table "public"."medreport_products_external_services_relations" drop constraint "medreport_products_connected_online_services_pkey";
|
||||
|
||||
-- alter table "public"."nonces" drop constraint "nonces_pkey";
|
||||
alter table "public"."nonces" drop constraint "nonces_pkey";
|
||||
|
||||
alter table "public"."notifications" drop constraint "notifications_pkey";
|
||||
|
||||
@@ -1876,11 +1876,12 @@ BEGIN
|
||||
END;$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION medreport.create_team_account(account_name text)
|
||||
RETURNS medreport.accounts
|
||||
LANGUAGE plpgsql
|
||||
SET search_path TO ''
|
||||
AS $function$declare
|
||||
create
|
||||
or replace function medreport.create_team_account (account_name text) returns medreport.accounts
|
||||
SECURITY DEFINER
|
||||
set
|
||||
search_path = '' as $$
|
||||
declare
|
||||
new_account medreport.accounts;
|
||||
begin
|
||||
if (not medreport.is_set('enable_team_accounts')) then
|
||||
@@ -1898,8 +1899,13 @@ begin
|
||||
|
||||
return new_account;
|
||||
|
||||
end;$function$
|
||||
;
|
||||
end;
|
||||
|
||||
$$ language plpgsql;
|
||||
|
||||
grant
|
||||
execute on function medreport.create_team_account (text) to authenticated,
|
||||
service_role;
|
||||
|
||||
CREATE OR REPLACE FUNCTION medreport.get_account_invitations(account_slug text)
|
||||
RETURNS TABLE(id integer, email character varying, account_id uuid, invited_by uuid, role character varying, created_at timestamp with time zone, updated_at timestamp with time zone, expires_at timestamp with time zone, inviter_name character varying, inviter_email character varying)
|
||||
@@ -3893,29 +3899,43 @@ to authenticated
|
||||
using ((account_id = auth.uid()));
|
||||
|
||||
|
||||
create policy "accounts_read"
|
||||
on "medreport"."accounts"
|
||||
as permissive
|
||||
for select
|
||||
to authenticated
|
||||
using (((( SELECT auth.uid() AS uid) = primary_owner_user_id) OR medreport.has_role_on_account(id) OR medreport.is_account_team_member(id)));
|
||||
create policy accounts_read on medreport.accounts for
|
||||
select
|
||||
to authenticated using (
|
||||
(
|
||||
(
|
||||
select
|
||||
auth.uid ()
|
||||
) = primary_owner_user_id
|
||||
)
|
||||
or medreport.has_role_on_account (id)
|
||||
or medreport.is_account_team_member (id)
|
||||
);
|
||||
|
||||
|
||||
create policy "accounts_self_update"
|
||||
on "medreport"."accounts"
|
||||
as permissive
|
||||
for update
|
||||
to authenticated
|
||||
using ((( SELECT auth.uid() AS uid) = primary_owner_user_id))
|
||||
with check ((( SELECT auth.uid() AS uid) = primary_owner_user_id));
|
||||
create policy accounts_self_update on medreport.accounts
|
||||
for update
|
||||
to authenticated using (
|
||||
(
|
||||
select
|
||||
auth.uid ()
|
||||
) = primary_owner_user_id
|
||||
)
|
||||
with
|
||||
check (
|
||||
(
|
||||
select
|
||||
auth.uid ()
|
||||
) = primary_owner_user_id
|
||||
);
|
||||
|
||||
|
||||
create policy "create_org_account"
|
||||
on "medreport"."accounts"
|
||||
as permissive
|
||||
for insert
|
||||
to authenticated
|
||||
with check ((medreport.is_set('enable_team_accounts'::text) AND (is_personal_account = false)));
|
||||
create policy create_org_account on medreport.accounts for insert to authenticated
|
||||
with
|
||||
check (
|
||||
medreport.is_set ('enable_team_accounts')
|
||||
and is_personal_account = false
|
||||
);
|
||||
|
||||
|
||||
create policy "restrict_mfa_accounts"
|
||||
@@ -5160,47 +5180,47 @@ revoke truncate on table "public"."medreport_products_external_services_relation
|
||||
|
||||
revoke update on table "public"."medreport_products_external_services_relations" from "service_role";
|
||||
|
||||
-- revoke delete on table "public"."nonces" from "anon";
|
||||
revoke delete on table "public"."nonces" from "anon";
|
||||
|
||||
-- revoke insert on table "public"."nonces" from "anon";
|
||||
revoke insert on table "public"."nonces" from "anon";
|
||||
|
||||
-- revoke references on table "public"."nonces" from "anon";
|
||||
revoke references on table "public"."nonces" from "anon";
|
||||
|
||||
-- revoke select on table "public"."nonces" from "anon";
|
||||
revoke select on table "public"."nonces" from "anon";
|
||||
|
||||
-- revoke trigger on table "public"."nonces" from "anon";
|
||||
revoke trigger on table "public"."nonces" from "anon";
|
||||
|
||||
-- revoke truncate on table "public"."nonces" from "anon";
|
||||
revoke truncate on table "public"."nonces" from "anon";
|
||||
|
||||
-- revoke update on table "public"."nonces" from "anon";
|
||||
revoke update on table "public"."nonces" from "anon";
|
||||
|
||||
-- revoke delete on table "public"."nonces" from "authenticated";
|
||||
revoke delete on table "public"."nonces" from "authenticated";
|
||||
|
||||
-- revoke insert on table "public"."nonces" from "authenticated";
|
||||
revoke insert on table "public"."nonces" from "authenticated";
|
||||
|
||||
-- revoke references on table "public"."nonces" from "authenticated";
|
||||
revoke references on table "public"."nonces" from "authenticated";
|
||||
|
||||
-- revoke select on table "public"."nonces" from "authenticated";
|
||||
revoke select on table "public"."nonces" from "authenticated";
|
||||
|
||||
-- revoke trigger on table "public"."nonces" from "authenticated";
|
||||
revoke trigger on table "public"."nonces" from "authenticated";
|
||||
|
||||
-- revoke truncate on table "public"."nonces" from "authenticated";
|
||||
revoke truncate on table "public"."nonces" from "authenticated";
|
||||
|
||||
-- revoke update on table "public"."nonces" from "authenticated";
|
||||
revoke update on table "public"."nonces" from "authenticated";
|
||||
|
||||
-- revoke delete on table "public"."nonces" from "service_role";
|
||||
revoke delete on table "public"."nonces" from "service_role";
|
||||
|
||||
-- revoke insert on table "public"."nonces" from "service_role";
|
||||
revoke insert on table "public"."nonces" from "service_role";
|
||||
|
||||
-- revoke references on table "public"."nonces" from "service_role";
|
||||
revoke references on table "public"."nonces" from "service_role";
|
||||
|
||||
-- revoke select on table "public"."nonces" from "service_role";
|
||||
revoke select on table "public"."nonces" from "service_role";
|
||||
|
||||
-- revoke trigger on table "public"."nonces" from "service_role";
|
||||
revoke trigger on table "public"."nonces" from "service_role";
|
||||
|
||||
-- revoke truncate on table "public"."nonces" from "service_role";
|
||||
revoke truncate on table "public"."nonces" from "service_role";
|
||||
|
||||
-- revoke update on table "public"."nonces" from "service_role";
|
||||
revoke update on table "public"."nonces" from "service_role";
|
||||
|
||||
revoke delete on table "public"."notifications" from "anon";
|
||||
|
||||
@@ -5410,7 +5430,7 @@ drop table "public"."medreport_products_analyses_relations";
|
||||
|
||||
drop table "public"."medreport_products_external_services_relations";
|
||||
|
||||
-- drop table "public"."nonces";
|
||||
drop table "public"."nonces";
|
||||
|
||||
drop table "public"."notifications";
|
||||
|
||||
|
||||
@@ -40,4 +40,8 @@ END;$function$
|
||||
grant execute on function medreport.has_consent_personal_data(uuid)
|
||||
to authenticated, anon;
|
||||
|
||||
-- we allow the authenticated role to execute functions in the medreport schema
|
||||
grant usage on schema medreport to authenticated;
|
||||
|
||||
-- we allow the service_role role to execute functions in the medreport schema
|
||||
grant usage on schema medreport to service_role;
|
||||
@@ -1 +1 @@
|
||||
alter type public.billing_provider add value 'montonio';
|
||||
alter type medreport.billing_provider add value 'montonio';
|
||||
|
||||
32
supabase/migrations/20250722110506_super_admin_fix.sql
Normal file
32
supabase/migrations/20250722110506_super_admin_fix.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
grant
|
||||
execute on function medreport.get_account_members (text) to authenticated,
|
||||
service_role;
|
||||
|
||||
create or replace function medreport.is_company_admin(account_slug text)
|
||||
returns boolean
|
||||
set search_path = ''
|
||||
language plpgsql
|
||||
as $$
|
||||
declare
|
||||
is_owner boolean;
|
||||
begin
|
||||
select exists (
|
||||
select 1
|
||||
from medreport.accounts_memberships am
|
||||
join medreport.accounts a on a.id = am.account_id
|
||||
where am.user_id = auth.uid()
|
||||
and am.account_role = 'owner'
|
||||
and a.slug = account_slug
|
||||
) into is_owner;
|
||||
|
||||
return is_owner;
|
||||
end;
|
||||
$$;
|
||||
|
||||
grant execute on function medreport.is_company_admin(text) to authenticated, service_role;
|
||||
|
||||
grant
|
||||
execute on function medreport.team_account_workspace (text) to authenticated,
|
||||
service_role;
|
||||
|
||||
grant execute on function medreport.get_account_invitations(text) to authenticated, service_role;
|
||||
67
supabase/migrations/20250723114200_company_params.sql
Normal file
67
supabase/migrations/20250723114200_company_params.sql
Normal file
@@ -0,0 +1,67 @@
|
||||
create table "medreport"."company_params" (
|
||||
"id" uuid not null default gen_random_uuid(),
|
||||
"benefit_occurance" text,
|
||||
"benefit_amount" numeric,
|
||||
"account_id" uuid,
|
||||
"created_at" timestamp with time zone default now(),
|
||||
"updated_at" timestamp with time zone default now(),
|
||||
"slug" text
|
||||
);
|
||||
|
||||
alter table "medreport"."company_params" enable row level security;
|
||||
|
||||
CREATE UNIQUE INDEX company_params_pkey ON medreport.company_params USING btree (id);
|
||||
|
||||
alter table "medreport"."company_params" add constraint "company_params_pkey" PRIMARY KEY using index "company_params_pkey";
|
||||
|
||||
alter table "medreport"."company_params" add constraint "company_params_account_id_fkey" FOREIGN KEY (account_id) REFERENCES medreport.accounts(id) ON DELETE CASCADE not valid;
|
||||
|
||||
alter table "medreport"."company_params" validate constraint "company_params_account_id_fkey";
|
||||
|
||||
set check_function_bodies = off;
|
||||
|
||||
CREATE OR REPLACE FUNCTION medreport.insert_company_params_on_new_company()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$begin
|
||||
insert into medreport.company_params (
|
||||
account_id,
|
||||
slug,
|
||||
benefit_occurance,
|
||||
benefit_amount
|
||||
) values (
|
||||
new.id,
|
||||
new.slug,
|
||||
null, -- or a default value like 'monthly'
|
||||
null -- or a default numeric like 0
|
||||
);
|
||||
return new;
|
||||
end;$function$
|
||||
;
|
||||
|
||||
grant execute on function medreport.insert_company_params_on_new_company() to authenticated,
|
||||
service_role;
|
||||
|
||||
CREATE TRIGGER trigger_create_company_params AFTER INSERT ON medreport.accounts FOR EACH ROW EXECUTE FUNCTION medreport.insert_company_params_on_new_company();
|
||||
|
||||
create policy "Allow select and update if user is account's primary owner"
|
||||
on medreport.company_params
|
||||
for all
|
||||
using (
|
||||
exists (
|
||||
select 1 from medreport.accounts
|
||||
where
|
||||
accounts.id = company_params.account_id
|
||||
and accounts.primary_owner_user_id = auth.uid()
|
||||
)
|
||||
)
|
||||
with check (
|
||||
exists (
|
||||
select 1 from medreport.accounts
|
||||
where
|
||||
accounts.id = company_params.account_id
|
||||
and accounts.primary_owner_user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
grant select, update on medreport.company_params to authenticated;
|
||||
16
supabase/sql/super-admin.sql
Normal file
16
supabase/sql/super-admin.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
-- Update your user role to Super Admin
|
||||
update auth.users set raw_app_meta_data='{"provider": "email", "providers": ["email"], "role": "super-admin" }' where email='test2@test.ee';
|
||||
|
||||
-- To create a new company user you need rows in Roles table
|
||||
INSERT INTO medreport.roles (name, hierarchy_level)
|
||||
VALUES
|
||||
('owner', 1),
|
||||
('member', 2);
|
||||
|
||||
-- Add role permissions
|
||||
insert into medreport.role_permissions (role, permission) values
|
||||
('owner', 'roles.manage'),
|
||||
('owner', 'billing.manage'),
|
||||
('owner', 'settings.manage'),
|
||||
('owner', 'members.manage'),
|
||||
('owner', 'invites.manage');
|
||||
Reference in New Issue
Block a user