diff --git a/apps/web/app/(dashboard)/home/[account]/_components/app-sidebar.tsx b/apps/web/app/(dashboard)/home/[account]/_components/app-sidebar.tsx
index 652474e27..455743b29 100644
--- a/apps/web/app/(dashboard)/home/[account]/_components/app-sidebar.tsx
+++ b/apps/web/app/(dashboard)/home/[account]/_components/app-sidebar.tsx
@@ -17,7 +17,7 @@ import {
import { Trans } from '@kit/ui/trans';
import { cn } from '@kit/ui/utils';
-import { ProfileDropdownContainer } from '~/(dashboard)/home/_components/personal-account-dropdown';
+import { ProfileAccountDropdownContainer } from '~/(dashboard)/home/_components/personal-account-dropdown';
import featureFlagsConfig from '~/config/feature-flags.config';
import pathsConfig from '~/config/paths.config';
@@ -89,7 +89,7 @@ function SidebarContainer(props: {
-
diff --git a/apps/web/app/(dashboard)/home/_components/home-sidebar.tsx b/apps/web/app/(dashboard)/home/_components/home-sidebar.tsx
index 77c2678dd..458e8eae0 100644
--- a/apps/web/app/(dashboard)/home/_components/home-sidebar.tsx
+++ b/apps/web/app/(dashboard)/home/_components/home-sidebar.tsx
@@ -5,7 +5,7 @@ import { cookies } from 'next/headers';
import { Sidebar, SidebarContent, SidebarNavigation } from '@kit/ui/sidebar';
import { HomeSidebarAccountSelector } from '~/(dashboard)/home/_components/home-sidebar-account-selector';
-import { ProfileDropdownContainer } from '~/(dashboard)/home/_components/personal-account-dropdown';
+import { ProfileAccountDropdownContainer } from '~/(dashboard)/home/_components/personal-account-dropdown';
import { loadUserWorkspace } from '~/(dashboard)/home/_lib/load-user-workspace';
import { personalAccountSidebarConfig } from '~/config/personal-account-sidebar.config';
@@ -25,7 +25,10 @@ export function HomeSidebar() {
diff --git a/apps/web/app/(dashboard)/home/_components/personal-account-dropdown.tsx b/apps/web/app/(dashboard)/home/_components/personal-account-dropdown.tsx
index deffa2ed4..dc1ee9f1d 100644
--- a/apps/web/app/(dashboard)/home/_components/personal-account-dropdown.tsx
+++ b/apps/web/app/(dashboard)/home/_components/personal-account-dropdown.tsx
@@ -5,9 +5,10 @@ import type { Session } from '@supabase/supabase-js';
import { PersonalAccountDropdown } from '@kit/accounts/personal-account-dropdown';
import { useSignOut } from '@kit/supabase/hooks/use-sign-out';
+import featuresFlagConfig from '~/config/feature-flags.config';
import pathsConfig from '~/config/paths.config';
-export function ProfileDropdownContainer(props: {
+export function ProfileAccountDropdownContainer(props: {
collapsed: boolean;
session: Session | null;
}) {
@@ -19,6 +20,9 @@ export function ProfileDropdownContainer(props: {
paths={{
home: pathsConfig.app.home,
}}
+ features={{
+ enableThemeToggle: featuresFlagConfig.enableThemeToggle,
+ }}
className={'w-full'}
showProfileName={!props.collapsed}
session={props.session}
diff --git a/apps/web/app/(marketing)/_components/site-header-account-section.tsx b/apps/web/app/(marketing)/_components/site-header-account-section.tsx
index d39f04976..02abd90f0 100644
--- a/apps/web/app/(marketing)/_components/site-header-account-section.tsx
+++ b/apps/web/app/(marketing)/_components/site-header-account-section.tsx
@@ -13,6 +13,7 @@ import { Button } from '@kit/ui/button';
import { If } from '@kit/ui/if';
import { Trans } from '@kit/ui/trans';
+import featuresFlagConfig from '~/config/feature-flags.config';
import pathsConfig from '~/config/paths.config';
export function SiteHeaderAccountSection(
@@ -38,6 +39,9 @@ function SuspendedPersonalAccountDropdown(props: { session: Session | null }) {
paths={{
home: pathsConfig.app.home,
}}
+ features={{
+ enableThemeToggle: featuresFlagConfig.enableThemeToggle,
+ }}
session={session}
signOutRequested={() => signOut.mutateAsync()}
/>
diff --git a/apps/web/app/(marketing)/_components/site-header.tsx b/apps/web/app/(marketing)/_components/site-header.tsx
index 8ea99589e..d24c8a0fb 100644
--- a/apps/web/app/(marketing)/_components/site-header.tsx
+++ b/apps/web/app/(marketing)/_components/site-header.tsx
@@ -1,5 +1,7 @@
import type { Session } from '@supabase/supabase-js';
+import { ModeToggle } from '@kit/ui/mode-toggle';
+
import { SiteHeaderAccountSection } from '~/(marketing)/_components/site-header-account-section';
import { SiteNavigation } from '~/(marketing)/_components/site-navigation';
import { AppLogo } from '~/components/app-logo';
@@ -17,13 +19,11 @@ export function SiteHeader(props: { session?: Session | null }) {
diff --git a/apps/web/app/join/page.tsx b/apps/web/app/join/page.tsx
index 889e62117..e47ed93d0 100644
--- a/apps/web/app/join/page.tsx
+++ b/apps/web/app/join/page.tsx
@@ -1,7 +1,11 @@
-import { notFound } from 'next/navigation';
+import { notFound, redirect } from 'next/navigation';
+import { Logger } from '@kit/shared/logger';
+import { requireAuth } from '@kit/supabase/require-auth';
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
+import { AcceptInvitationContainer } from '@kit/team-accounts/components';
+import pathsConfig from '~/config/paths.config';
import { withI18n } from '~/lib/i18n/with-i18n';
interface Context {
@@ -10,23 +14,98 @@ interface Context {
};
}
-export const metadata = {
- title: `Join Organization`,
+export const generateMetadata = () => {
+ return {
+ title: 'Join Team Account',
+ };
};
async function JoinTeamAccountPage({ searchParams }: Context) {
const token = searchParams.invite_token;
- const data = await getInviteDataFromInviteToken(token);
- if (!data) {
+ // no token, redirect to 404
+ if (!token) {
notFound();
}
- return <>>;
+ const client = getSupabaseServerComponentClient();
+ const session = await requireAuth(client);
+
+ // if the user is not logged in or there is an error
+ // redirect to the sign up page with the invite token
+ // so that they will get back to this page after signing up
+ if (session.error ?? !session.data) {
+ redirect(pathsConfig.auth.signUp + '?invite_token=' + token);
+ }
+
+ // the user is logged in, we can now check if the token is valid
+ const invitation = await getInviteDataFromInviteToken(token);
+
+ if (!invitation) {
+ notFound();
+ }
+
+ // we need to verify the user isn't already in the account
+ const isInAccount = await isCurrentUserAlreadyInAccount(
+ invitation.account.id,
+ );
+
+ if (isInAccount) {
+ Logger.warn(
+ {
+ name: 'join-team-account',
+ accountId: invitation.account.id,
+ userId: session.data.user.id,
+ },
+ 'User is already in the account. Redirecting to account page.',
+ );
+
+ // if the user is already in the account redirect to the home page
+ redirect(pathsConfig.app.home);
+ }
+
+ // if the user decides to sign in with a different account
+ // we redirect them to the sign in page with the invite token
+ const signOutNext = pathsConfig.auth.signIn + '?invite_token=' + token;
+
+ // once the user accepts the invitation, we redirect them to the account home page
+ const accountHome = pathsConfig.app.accountHome.replace(
+ '[account]',
+ invitation.account.slug,
+ );
+
+ return (
+
+ );
}
export default withI18n(JoinTeamAccountPage);
+/**
+ * Verifies that the current user is not already in the account by
+ * reading the document from the `accounts` table. If the user can read it
+ * it means they are already in the account.
+ * @param accountId
+ */
+async function isCurrentUserAlreadyInAccount(accountId: string) {
+ const client = getSupabaseServerComponentClient();
+
+ const { data } = await client
+ .from('accounts')
+ .select('id')
+ .eq('id', accountId)
+ .maybeSingle();
+
+ return !!data?.id;
+}
+
async function getInviteDataFromInviteToken(token: string) {
// we use an admin client to be able to read the pending membership
// without having to be logged in
@@ -34,7 +113,18 @@ async function getInviteDataFromInviteToken(token: string) {
const { data: invitation, error } = await adminClient
.from('invitations')
- .select()
+ .select<
+ string,
+ {
+ id: string;
+ account: {
+ id: string;
+ name: string;
+ slug: string;
+ picture_url: string;
+ };
+ }
+ >('id, account: account_id !inner (id, name, slug, picture_url)')
.eq('invite_token', token)
.single();
diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx
index fefb88ed7..2c54f38b7 100644
--- a/apps/web/app/layout.tsx
+++ b/apps/web/app/layout.tsx
@@ -40,7 +40,7 @@ function getClassName() {
const theme = themeCookie ?? appConfig.theme;
const dark = theme === 'dark';
- return cn('antialiased', {
+ return cn('min-h-screen bg-background antialiased', {
dark,
[sans.className]: true,
});
diff --git a/apps/web/components/root-providers.tsx b/apps/web/components/root-providers.tsx
index c19cd04dc..b81244144 100644
--- a/apps/web/components/root-providers.tsx
+++ b/apps/web/components/root-providers.tsx
@@ -2,10 +2,12 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryStreamedHydration } from '@tanstack/react-query-next-experimental';
+import { ThemeProvider } from 'next-themes';
import { I18nProvider } from '@kit/i18n/provider';
import { AuthChangeListener } from '@kit/supabase/components/auth-change-listener';
+import appConfig from '~/config/app.config';
import pathsConfig from '~/config/paths.config';
import { i18nResolver } from '~/lib/i18n/i18n.resolver';
@@ -22,7 +24,14 @@ export function RootProviders({
- {children}
+
+ {children}
+
diff --git a/apps/web/config/feature-flags.config.ts b/apps/web/config/feature-flags.config.ts
index 43dbbcade..bc2bc2acd 100644
--- a/apps/web/config/feature-flags.config.ts
+++ b/apps/web/config/feature-flags.config.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
const FeatureFlagsSchema = z.object({
- enableThemeSwitcher: z.boolean(),
+ enableThemeToggle: z.boolean(),
enableAccountDeletion: z.boolean(),
enableTeamDeletion: z.boolean(),
enableTeamAccounts: z.boolean(),
@@ -11,7 +11,7 @@ const FeatureFlagsSchema = z.object({
});
const featuresFlagConfig = FeatureFlagsSchema.parse({
- enableThemeSwitcher: true,
+ enableThemeToggle: true,
enableAccountDeletion: getBoolean(
process.env.NEXT_PUBLIC_ENABLE_ACCOUNT_DELETION,
false,
diff --git a/apps/web/public/locales/en/teams.json b/apps/web/public/locales/en/teams.json
index a58b698fd..5a44ba639 100644
--- a/apps/web/public/locales/en/teams.json
+++ b/apps/web/public/locales/en/teams.json
@@ -121,5 +121,12 @@
"updateRoleErrorMessage": "We encountered an error updating the role of the selected member. Please try again.",
"searchInvitations": "Search Invitations",
"updateInvitation": "Update Invitation",
- "removeInvitation": "Remove Invitation"
+ "removeInvitation": "Remove Invitation",
+ "acceptInvitation": "Accept Invitation",
+ "signInWithDifferentAccount": "Sign in with a different account",
+ "signInWithDifferentAccountDescription": "If you wish to accept the invitation with a different account, please sign out and back in with the account you wish to use.",
+ "acceptInvitationHeading": "Accept Invitation to join {{accountName}}",
+ "acceptInvitationDescription": "You have been invited to join the team {{accountName}}. If you wish to accept the invitation, please click the button below.",
+ "joinTeam": "Join {{accountName}}",
+ "joiningTeam": "Joining team..."
}
diff --git a/packages/features/accounts/package.json b/packages/features/accounts/package.json
index f1799554e..05bbe21b1 100644
--- a/packages/features/accounts/package.json
+++ b/packages/features/accounts/package.json
@@ -29,6 +29,7 @@
"@radix-ui/react-icons": "^1.3.0",
"@tanstack/react-query": "5.28.6",
"lucide-react": "^0.363.0",
+ "next-themes": "0.3.0",
"react-hook-form": "^7.51.2",
"react-i18next": "^14.1.0",
"sonner": "^1.4.41",
diff --git a/packages/features/accounts/src/components/personal-account-dropdown.tsx b/packages/features/accounts/src/components/personal-account-dropdown.tsx
index 465ac7bb6..e0cc39f3c 100644
--- a/packages/features/accounts/src/components/personal-account-dropdown.tsx
+++ b/packages/features/accounts/src/components/personal-account-dropdown.tsx
@@ -22,6 +22,7 @@ import {
DropdownMenuTrigger,
} from '@kit/ui/dropdown-menu';
import { If } from '@kit/ui/if';
+import { SubMenuModeToggle } from '@kit/ui/mode-toggle';
import { ProfileAvatar } from '@kit/ui/profile-avatar';
import { Trans } from '@kit/ui/trans';
import { cn } from '@kit/ui/utils';
@@ -34,6 +35,7 @@ export function PersonalAccountDropdown({
signOutRequested,
showProfileName,
paths,
+ features,
}: {
className?: string;
session: Session | null;
@@ -42,6 +44,9 @@ export function PersonalAccountDropdown({
paths: {
home: string;
};
+ features: {
+ enableThemeToggle: boolean;
+ };
}) {
const { data: personalAccountData } = usePersonalAccountData();
const authUser = session?.user;
@@ -156,6 +161,12 @@ export function PersonalAccountDropdown({
+
+
+
+
+
+
-
+
@@ -42,7 +40,7 @@ export function SignUpMethodsContainer(props: {
-
+
);
}
+
+function getCallbackUrl(props: {
+ paths: {
+ callback: string;
+ appHome: string;
+ };
+
+ inviteToken?: string;
+}) {
+ if (!isBrowser()) {
+ return '';
+ }
+
+ const redirectPath = props.paths.callback;
+ const origin = window.location.origin;
+ const url = new URL(redirectPath, origin);
+
+ if (props.inviteToken) {
+ url.searchParams.set('invite_token', props.inviteToken);
+ }
+
+ return url.href;
+}
diff --git a/packages/features/team-accounts/src/components/index.ts b/packages/features/team-accounts/src/components/index.ts
index 69c53e978..bf908ef14 100644
--- a/packages/features/team-accounts/src/components/index.ts
+++ b/packages/features/team-accounts/src/components/index.ts
@@ -3,3 +3,4 @@ export * from './members/invite-members-dialog-container';
export * from './settings/team-account-danger-zone';
export * from './invitations/account-invitations-table';
export * from './settings/team-account-settings-container';
+export * from './invitations/accept-invitation-container';
diff --git a/packages/features/team-accounts/src/components/invitations/accept-invitation-container.tsx b/packages/features/team-accounts/src/components/invitations/accept-invitation-container.tsx
new file mode 100644
index 000000000..ead04bbfc
--- /dev/null
+++ b/packages/features/team-accounts/src/components/invitations/accept-invitation-container.tsx
@@ -0,0 +1,85 @@
+import Image from 'next/image';
+
+import { Heading } from '@kit/ui/heading';
+import { If } from '@kit/ui/if';
+import { Separator } from '@kit/ui/separator';
+import { Trans } from '@kit/ui/trans';
+
+import { acceptInvitationAction } from '../../server/actions/team-invitations-server-actions';
+import { InvitationSubmitButton } from './invitation-submit-button';
+import { SignOutInvitationButton } from './sign-out-invitation-button';
+
+export function AcceptInvitationContainer(props: {
+ inviteToken: string;
+
+ invitation: {
+ id: string;
+
+ account: {
+ name: string;
+ id: string;
+ picture_url: string | null;
+ };
+ };
+
+ paths: {
+ signOutNext: string;
+ accountHome: string;
+ };
+}) {
+ return (
+
+
+
+
+
+
+ {(url) => (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/packages/features/team-accounts/src/components/invitations/invitation-submit-button.tsx b/packages/features/team-accounts/src/components/invitations/invitation-submit-button.tsx
new file mode 100644
index 000000000..e0060367e
--- /dev/null
+++ b/packages/features/team-accounts/src/components/invitations/invitation-submit-button.tsx
@@ -0,0 +1,21 @@
+'use client';
+
+import { useFormStatus } from 'react-dom';
+
+import { Button } from '@kit/ui/button';
+import { Trans } from '@kit/ui/trans';
+
+export function InvitationSubmitButton(props: { accountName: string }) {
+ const { pending } = useFormStatus();
+
+ return (
+
+ );
+}
diff --git a/packages/features/team-accounts/src/components/invitations/sign-out-invitation-button.tsx b/packages/features/team-accounts/src/components/invitations/sign-out-invitation-button.tsx
new file mode 100644
index 000000000..d9e1b15dc
--- /dev/null
+++ b/packages/features/team-accounts/src/components/invitations/sign-out-invitation-button.tsx
@@ -0,0 +1,25 @@
+'use client';
+
+import { useSignOut } from '@kit/supabase/hooks/use-sign-out';
+import { Button } from '@kit/ui/button';
+import { Trans } from '@kit/ui/trans';
+
+export function SignOutInvitationButton(
+ props: React.PropsWithChildren<{
+ nextPath: string;
+ }>,
+) {
+ const signOut = useSignOut();
+
+ return (
+
+ );
+}
diff --git a/packages/features/team-accounts/src/components/members/role-badge.tsx b/packages/features/team-accounts/src/components/members/role-badge.tsx
index 2a9ee37e4..2e749fd81 100644
--- a/packages/features/team-accounts/src/components/members/role-badge.tsx
+++ b/packages/features/team-accounts/src/components/members/role-badge.tsx
@@ -9,8 +9,9 @@ type Role = Database['public']['Enums']['account_role'];
const roleClassNameBuilder = cva('font-medium capitalize', {
variants: {
role: {
- owner: 'bg-primary',
- member: 'bg-blue-50 text-blue-500 dark:bg-blue-500/10',
+ owner: '',
+ member:
+ 'bg-blue-50 hover:bg-blue-50 text-blue-500 dark:bg-blue-500/10 dark:hover:bg-blue-500/10',
},
},
});
diff --git a/packages/features/team-accounts/src/components/members/transfer-ownership-dialog.tsx b/packages/features/team-accounts/src/components/members/transfer-ownership-dialog.tsx
index da5ae08ea..d3e76a96d 100644
--- a/packages/features/team-accounts/src/components/members/transfer-ownership-dialog.tsx
+++ b/packages/features/team-accounts/src/components/members/transfer-ownership-dialog.tsx
@@ -6,14 +6,16 @@ import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
-import { Button } from '@kit/ui/button';
import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogHeader,
- DialogTitle,
-} from '@kit/ui/dialog';
+ AlertDialog,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from '@kit/ui/alert-dialog';
+import { Button } from '@kit/ui/button';
import {
Form,
FormControl,
@@ -38,17 +40,17 @@ export const TransferOwnershipDialog: React.FC<{
targetDisplayName: string;
}> = ({ isOpen, setIsOpen, targetDisplayName, accountId, userId }) => {
return (
-
+
+
);
};
@@ -100,7 +102,7 @@ function TransferOrganizationOwnershipForm({
return (
+
+
);
diff --git a/packages/features/team-accounts/src/components/members/update-member-role-dialog.tsx b/packages/features/team-accounts/src/components/members/update-member-role-dialog.tsx
index f29ff4bd7..5e2387f95 100644
--- a/packages/features/team-accounts/src/components/members/update-member-role-dialog.tsx
+++ b/packages/features/team-accounts/src/components/members/update-member-role-dialog.tsx
@@ -124,7 +124,7 @@ function UpdateMemberForm({
render={({ field }) => {
return (
- {t('memberRole')}
+ {t('roleLabel')}
) {
- const { data, error } = await client.auth.getUser();
+export async function acceptInvitationAction(data: FormData) {
+ const client = getSupabaseServerActionClient();
- if (error ?? !data.user) {
+ const { inviteToken, nextPath } = AcceptInvitationSchema.parse(
+ Object.fromEntries(data),
+ );
+
+ const { user } = await assertSession(client);
+
+ const service = new AccountInvitationsService(client);
+
+ await service.acceptInvitationToTeam({
+ adminClient: getSupabaseServerActionClient({ admin: true }),
+ inviteToken,
+ userId: user.id,
+ });
+
+ return redirect(nextPath);
+}
+
+async function assertSession(client: SupabaseClient) {
+ const { error, data } = await requireAuth(client);
+
+ if (error) {
throw new Error(`Authentication required`);
}
+
+ return data;
}
diff --git a/packages/features/team-accounts/src/server/services/account-invitations.service.ts b/packages/features/team-accounts/src/server/services/account-invitations.service.ts
index 738ec9503..d28f9ff6d 100644
--- a/packages/features/team-accounts/src/server/services/account-invitations.service.ts
+++ b/packages/features/team-accounts/src/server/services/account-invitations.service.ts
@@ -6,6 +6,7 @@ import { z } from 'zod';
import { Mailer } from '@kit/mailers';
import { Logger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
+import { requireAuth } from '@kit/supabase/require-auth';
import { DeleteInvitationSchema } from '../../schema/delete-invitation.schema';
import { InviteMembersSchema } from '../../schema/invite-members.schema';
@@ -206,8 +207,28 @@ export class AccountInvitationsService {
);
}
+ /**
+ * Accepts an invitation to join a team.
+ */
+ async acceptInvitationToTeam(params: {
+ userId: string;
+ inviteToken: string;
+ adminClient: SupabaseClient;
+ }) {
+ const { error, data } = await params.adminClient.rpc('accept_invitation', {
+ token: params.inviteToken,
+ user_id: params.userId,
+ });
+
+ if (error) {
+ throw error;
+ }
+
+ return data;
+ }
+
private async getUser() {
- const { data, error } = await this.client.auth.getUser();
+ const { data, error } = await requireAuth(this.client);
if (error ?? !data) {
throw new Error('Authentication required');
@@ -217,6 +238,6 @@ export class AccountInvitationsService {
}
private getInvitationLink(token: string) {
- return new URL(env.invitePath, env.siteURL).href + `?token=${token}`;
+ return new URL(env.siteURL, env.siteURL).href + `?invite_token=${token}`;
}
}
diff --git a/packages/supabase/src/database.types.ts b/packages/supabase/src/database.types.ts
index fb0a9b170..d87daea35 100644
--- a/packages/supabase/src/database.types.ts
+++ b/packages/supabase/src/database.types.ts
@@ -4,965 +4,980 @@ export type Json =
| boolean
| null
| { [key: string]: Json | undefined }
- | Json[];
+ | Json[]
export type Database = {
graphql_public: {
Tables: {
- [_ in never]: never;
- };
+ [_ in never]: never
+ }
Views: {
- [_ in never]: never;
- };
+ [_ in never]: never
+ }
Functions: {
graphql: {
Args: {
- operationName?: string;
- query?: string;
- variables?: Json;
- extensions?: Json;
- };
- Returns: Json;
- };
- };
+ operationName?: string
+ query?: string
+ variables?: Json
+ extensions?: Json
+ }
+ Returns: Json
+ }
+ }
Enums: {
- [_ in never]: never;
- };
+ [_ in never]: never
+ }
CompositeTypes: {
- [_ in never]: never;
- };
- };
+ [_ in never]: never
+ }
+ }
public: {
Tables: {
account_roles: {
Row: {
- account_id: string;
- id: number;
- role: Database['public']['Enums']['account_role'];
- };
+ account_id: string
+ id: number
+ role: Database["public"]["Enums"]["account_role"]
+ }
Insert: {
- account_id: string;
- id?: number;
- role: Database['public']['Enums']['account_role'];
- };
+ account_id: string
+ id?: number
+ role: Database["public"]["Enums"]["account_role"]
+ }
Update: {
- account_id?: string;
- id?: number;
- role?: Database['public']['Enums']['account_role'];
- };
+ account_id?: string
+ id?: number
+ role?: Database["public"]["Enums"]["account_role"]
+ }
Relationships: [
{
- foreignKeyName: 'account_roles_account_id_fkey';
- columns: ['account_id'];
- isOneToOne: false;
- referencedRelation: 'accounts';
- referencedColumns: ['id'];
+ foreignKeyName: "account_roles_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "accounts"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'account_roles_account_id_fkey';
- columns: ['account_id'];
- isOneToOne: false;
- referencedRelation: 'user_account_workspace';
- referencedColumns: ['id'];
+ foreignKeyName: "account_roles_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "user_account_workspace"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'account_roles_account_id_fkey';
- columns: ['account_id'];
- isOneToOne: false;
- referencedRelation: 'user_accounts';
- referencedColumns: ['id'];
+ foreignKeyName: "account_roles_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "user_accounts"
+ referencedColumns: ["id"]
},
- ];
- };
+ ]
+ }
accounts: {
Row: {
- created_at: string | null;
- created_by: string | null;
- email: string | null;
- id: string;
- is_personal_account: boolean;
- name: string;
- picture_url: string | null;
- primary_owner_user_id: string;
- slug: string | null;
- updated_at: string | null;
- updated_by: string | null;
- };
+ created_at: string | null
+ created_by: string | null
+ email: string | null
+ id: string
+ is_personal_account: boolean
+ name: string
+ picture_url: string | null
+ primary_owner_user_id: string
+ slug: string | null
+ updated_at: string | null
+ updated_by: string | null
+ }
Insert: {
- created_at?: string | null;
- created_by?: string | null;
- email?: string | null;
- id?: string;
- is_personal_account?: boolean;
- name: string;
- picture_url?: string | null;
- primary_owner_user_id?: string;
- slug?: string | null;
- updated_at?: string | null;
- updated_by?: string | null;
- };
+ created_at?: string | null
+ created_by?: string | null
+ email?: string | null
+ id?: string
+ is_personal_account?: boolean
+ name: string
+ picture_url?: string | null
+ primary_owner_user_id?: string
+ slug?: string | null
+ updated_at?: string | null
+ updated_by?: string | null
+ }
Update: {
- created_at?: string | null;
- created_by?: string | null;
- email?: string | null;
- id?: string;
- is_personal_account?: boolean;
- name?: string;
- picture_url?: string | null;
- primary_owner_user_id?: string;
- slug?: string | null;
- updated_at?: string | null;
- updated_by?: string | null;
- };
+ created_at?: string | null
+ created_by?: string | null
+ email?: string | null
+ id?: string
+ is_personal_account?: boolean
+ name?: string
+ picture_url?: string | null
+ primary_owner_user_id?: string
+ slug?: string | null
+ updated_at?: string | null
+ updated_by?: string | null
+ }
Relationships: [
{
- foreignKeyName: 'accounts_created_by_fkey';
- columns: ['created_by'];
- isOneToOne: false;
- referencedRelation: 'users';
- referencedColumns: ['id'];
+ foreignKeyName: "accounts_created_by_fkey"
+ columns: ["created_by"]
+ isOneToOne: false
+ referencedRelation: "users"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'accounts_primary_owner_user_id_fkey';
- columns: ['primary_owner_user_id'];
- isOneToOne: false;
- referencedRelation: 'users';
- referencedColumns: ['id'];
+ foreignKeyName: "accounts_primary_owner_user_id_fkey"
+ columns: ["primary_owner_user_id"]
+ isOneToOne: false
+ referencedRelation: "users"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'accounts_updated_by_fkey';
- columns: ['updated_by'];
- isOneToOne: false;
- referencedRelation: 'users';
- referencedColumns: ['id'];
+ foreignKeyName: "accounts_updated_by_fkey"
+ columns: ["updated_by"]
+ isOneToOne: false
+ referencedRelation: "users"
+ referencedColumns: ["id"]
},
- ];
- };
+ ]
+ }
accounts_memberships: {
Row: {
- account_id: string;
- account_role: Database['public']['Enums']['account_role'];
- created_at: string;
- created_by: string | null;
- updated_at: string;
- updated_by: string | null;
- user_id: string;
- };
+ account_id: string
+ account_role: Database["public"]["Enums"]["account_role"]
+ created_at: string
+ created_by: string | null
+ updated_at: string
+ updated_by: string | null
+ user_id: string
+ }
Insert: {
- account_id: string;
- account_role: Database['public']['Enums']['account_role'];
- created_at?: string;
- created_by?: string | null;
- updated_at?: string;
- updated_by?: string | null;
- user_id: string;
- };
+ account_id: string
+ account_role: Database["public"]["Enums"]["account_role"]
+ created_at?: string
+ created_by?: string | null
+ updated_at?: string
+ updated_by?: string | null
+ user_id: string
+ }
Update: {
- account_id?: string;
- account_role?: Database['public']['Enums']['account_role'];
- created_at?: string;
- created_by?: string | null;
- updated_at?: string;
- updated_by?: string | null;
- user_id?: string;
- };
+ account_id?: string
+ account_role?: Database["public"]["Enums"]["account_role"]
+ created_at?: string
+ created_by?: string | null
+ updated_at?: string
+ updated_by?: string | null
+ user_id?: string
+ }
Relationships: [
{
- foreignKeyName: 'accounts_memberships_account_id_fkey';
- columns: ['account_id'];
- isOneToOne: false;
- referencedRelation: 'accounts';
- referencedColumns: ['id'];
+ foreignKeyName: "accounts_memberships_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "accounts"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'accounts_memberships_account_id_fkey';
- columns: ['account_id'];
- isOneToOne: false;
- referencedRelation: 'user_account_workspace';
- referencedColumns: ['id'];
+ foreignKeyName: "accounts_memberships_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "user_account_workspace"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'accounts_memberships_account_id_fkey';
- columns: ['account_id'];
- isOneToOne: false;
- referencedRelation: 'user_accounts';
- referencedColumns: ['id'];
+ foreignKeyName: "accounts_memberships_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "user_accounts"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'accounts_memberships_created_by_fkey';
- columns: ['created_by'];
- isOneToOne: false;
- referencedRelation: 'users';
- referencedColumns: ['id'];
+ foreignKeyName: "accounts_memberships_created_by_fkey"
+ columns: ["created_by"]
+ isOneToOne: false
+ referencedRelation: "users"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'accounts_memberships_updated_by_fkey';
- columns: ['updated_by'];
- isOneToOne: false;
- referencedRelation: 'users';
- referencedColumns: ['id'];
+ foreignKeyName: "accounts_memberships_updated_by_fkey"
+ columns: ["updated_by"]
+ isOneToOne: false
+ referencedRelation: "users"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'accounts_memberships_user_id_fkey';
- columns: ['user_id'];
- isOneToOne: false;
- referencedRelation: 'users';
- referencedColumns: ['id'];
+ foreignKeyName: "accounts_memberships_user_id_fkey"
+ columns: ["user_id"]
+ isOneToOne: false
+ referencedRelation: "users"
+ referencedColumns: ["id"]
},
- ];
- };
+ ]
+ }
billing_customers: {
Row: {
- account_id: string;
- customer_id: string;
- email: string | null;
- id: number;
- provider: Database['public']['Enums']['billing_provider'];
- };
+ account_id: string
+ customer_id: string
+ email: string | null
+ id: number
+ provider: Database["public"]["Enums"]["billing_provider"]
+ }
Insert: {
- account_id: string;
- customer_id: string;
- email?: string | null;
- id?: number;
- provider: Database['public']['Enums']['billing_provider'];
- };
+ account_id: string
+ customer_id: string
+ email?: string | null
+ id?: number
+ provider: Database["public"]["Enums"]["billing_provider"]
+ }
Update: {
- account_id?: string;
- customer_id?: string;
- email?: string | null;
- id?: number;
- provider?: Database['public']['Enums']['billing_provider'];
- };
+ account_id?: string
+ customer_id?: string
+ email?: string | null
+ id?: number
+ provider?: Database["public"]["Enums"]["billing_provider"]
+ }
Relationships: [
{
- foreignKeyName: 'billing_customers_account_id_fkey';
- columns: ['account_id'];
- isOneToOne: false;
- referencedRelation: 'accounts';
- referencedColumns: ['id'];
+ foreignKeyName: "billing_customers_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "accounts"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'billing_customers_account_id_fkey';
- columns: ['account_id'];
- isOneToOne: false;
- referencedRelation: 'user_account_workspace';
- referencedColumns: ['id'];
+ foreignKeyName: "billing_customers_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "user_account_workspace"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'billing_customers_account_id_fkey';
- columns: ['account_id'];
- isOneToOne: false;
- referencedRelation: 'user_accounts';
- referencedColumns: ['id'];
+ foreignKeyName: "billing_customers_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "user_accounts"
+ referencedColumns: ["id"]
},
- ];
- };
+ ]
+ }
config: {
Row: {
- billing_provider: Database['public']['Enums']['billing_provider'];
- enable_account_billing: boolean;
- enable_organization_accounts: boolean;
- enable_organization_billing: boolean;
- };
+ billing_provider: Database["public"]["Enums"]["billing_provider"]
+ enable_account_billing: boolean
+ enable_organization_accounts: boolean
+ enable_organization_billing: boolean
+ }
Insert: {
- billing_provider?: Database['public']['Enums']['billing_provider'];
- enable_account_billing?: boolean;
- enable_organization_accounts?: boolean;
- enable_organization_billing?: boolean;
- };
+ billing_provider?: Database["public"]["Enums"]["billing_provider"]
+ enable_account_billing?: boolean
+ enable_organization_accounts?: boolean
+ enable_organization_billing?: boolean
+ }
Update: {
- billing_provider?: Database['public']['Enums']['billing_provider'];
- enable_account_billing?: boolean;
- enable_organization_accounts?: boolean;
- enable_organization_billing?: boolean;
- };
- Relationships: [];
- };
+ billing_provider?: Database["public"]["Enums"]["billing_provider"]
+ enable_account_billing?: boolean
+ enable_organization_accounts?: boolean
+ enable_organization_billing?: boolean
+ }
+ Relationships: []
+ }
invitations: {
Row: {
- account_id: string;
- created_at: string;
- email: string;
- expires_at: string;
- id: number;
- invite_token: string;
- invited_by: string;
- role: Database['public']['Enums']['account_role'];
- updated_at: string;
- };
+ account_id: string
+ created_at: string
+ email: string
+ expires_at: string
+ id: number
+ invite_token: string
+ invited_by: string
+ role: Database["public"]["Enums"]["account_role"]
+ updated_at: string
+ }
Insert: {
- account_id: string;
- created_at?: string;
- email: string;
- expires_at?: string;
- id?: number;
- invite_token: string;
- invited_by: string;
- role: Database['public']['Enums']['account_role'];
- updated_at?: string;
- };
+ account_id: string
+ created_at?: string
+ email: string
+ expires_at?: string
+ id?: number
+ invite_token: string
+ invited_by: string
+ role: Database["public"]["Enums"]["account_role"]
+ updated_at?: string
+ }
Update: {
- account_id?: string;
- created_at?: string;
- email?: string;
- expires_at?: string;
- id?: number;
- invite_token?: string;
- invited_by?: string;
- role?: Database['public']['Enums']['account_role'];
- updated_at?: string;
- };
+ account_id?: string
+ created_at?: string
+ email?: string
+ expires_at?: string
+ id?: number
+ invite_token?: string
+ invited_by?: string
+ role?: Database["public"]["Enums"]["account_role"]
+ updated_at?: string
+ }
Relationships: [
{
- foreignKeyName: 'invitations_account_id_fkey';
- columns: ['account_id'];
- isOneToOne: false;
- referencedRelation: 'accounts';
- referencedColumns: ['id'];
+ foreignKeyName: "invitations_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "accounts"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'invitations_account_id_fkey';
- columns: ['account_id'];
- isOneToOne: false;
- referencedRelation: 'user_account_workspace';
- referencedColumns: ['id'];
+ foreignKeyName: "invitations_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "user_account_workspace"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'invitations_account_id_fkey';
- columns: ['account_id'];
- isOneToOne: false;
- referencedRelation: 'user_accounts';
- referencedColumns: ['id'];
+ foreignKeyName: "invitations_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "user_accounts"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'invitations_invited_by_fkey';
- columns: ['invited_by'];
- isOneToOne: false;
- referencedRelation: 'users';
- referencedColumns: ['id'];
+ foreignKeyName: "invitations_invited_by_fkey"
+ columns: ["invited_by"]
+ isOneToOne: false
+ referencedRelation: "users"
+ referencedColumns: ["id"]
},
- ];
- };
+ ]
+ }
role_permissions: {
Row: {
- id: number;
- permission: Database['public']['Enums']['app_permissions'];
- role: Database['public']['Enums']['account_role'];
- };
+ id: number
+ permission: Database["public"]["Enums"]["app_permissions"]
+ role: Database["public"]["Enums"]["account_role"]
+ }
Insert: {
- id?: number;
- permission: Database['public']['Enums']['app_permissions'];
- role: Database['public']['Enums']['account_role'];
- };
+ id?: number
+ permission: Database["public"]["Enums"]["app_permissions"]
+ role: Database["public"]["Enums"]["account_role"]
+ }
Update: {
- id?: number;
- permission?: Database['public']['Enums']['app_permissions'];
- role?: Database['public']['Enums']['account_role'];
- };
- Relationships: [];
- };
+ id?: number
+ permission?: Database["public"]["Enums"]["app_permissions"]
+ role?: Database["public"]["Enums"]["account_role"]
+ }
+ Relationships: []
+ }
subscriptions: {
Row: {
- account_id: string;
- active: boolean;
- billing_customer_id: number;
- billing_provider: Database['public']['Enums']['billing_provider'];
- cancel_at_period_end: boolean;
- created_at: string;
- currency: string;
- id: string;
- interval: string;
- interval_count: number;
- period_ends_at: string | null;
- period_starts_at: string | null;
- price_amount: number | null;
- product_id: string;
- status: Database['public']['Enums']['subscription_status'];
- trial_ends_at: string | null;
- trial_starts_at: string | null;
- updated_at: string;
- variant_id: string;
- };
+ account_id: string
+ active: boolean
+ billing_customer_id: number
+ billing_provider: Database["public"]["Enums"]["billing_provider"]
+ cancel_at_period_end: boolean
+ created_at: string
+ currency: string
+ id: string
+ interval: string
+ interval_count: number
+ period_ends_at: string | null
+ period_starts_at: string | null
+ price_amount: number | null
+ product_id: string
+ status: Database["public"]["Enums"]["subscription_status"]
+ trial_ends_at: string | null
+ trial_starts_at: string | null
+ updated_at: string
+ variant_id: string
+ }
Insert: {
- account_id: string;
- active: boolean;
- billing_customer_id: number;
- billing_provider: Database['public']['Enums']['billing_provider'];
- cancel_at_period_end: boolean;
- created_at?: string;
- currency: string;
- id: string;
- interval: string;
- interval_count: number;
- period_ends_at?: string | null;
- period_starts_at?: string | null;
- price_amount?: number | null;
- product_id: string;
- status: Database['public']['Enums']['subscription_status'];
- trial_ends_at?: string | null;
- trial_starts_at?: string | null;
- updated_at?: string;
- variant_id: string;
- };
+ account_id: string
+ active: boolean
+ billing_customer_id: number
+ billing_provider: Database["public"]["Enums"]["billing_provider"]
+ cancel_at_period_end: boolean
+ created_at?: string
+ currency: string
+ id: string
+ interval: string
+ interval_count: number
+ period_ends_at?: string | null
+ period_starts_at?: string | null
+ price_amount?: number | null
+ product_id: string
+ status: Database["public"]["Enums"]["subscription_status"]
+ trial_ends_at?: string | null
+ trial_starts_at?: string | null
+ updated_at?: string
+ variant_id: string
+ }
Update: {
- account_id?: string;
- active?: boolean;
- billing_customer_id?: number;
- billing_provider?: Database['public']['Enums']['billing_provider'];
- cancel_at_period_end?: boolean;
- created_at?: string;
- currency?: string;
- id?: string;
- interval?: string;
- interval_count?: number;
- period_ends_at?: string | null;
- period_starts_at?: string | null;
- price_amount?: number | null;
- product_id?: string;
- status?: Database['public']['Enums']['subscription_status'];
- trial_ends_at?: string | null;
- trial_starts_at?: string | null;
- updated_at?: string;
- variant_id?: string;
- };
+ account_id?: string
+ active?: boolean
+ billing_customer_id?: number
+ billing_provider?: Database["public"]["Enums"]["billing_provider"]
+ cancel_at_period_end?: boolean
+ created_at?: string
+ currency?: string
+ id?: string
+ interval?: string
+ interval_count?: number
+ period_ends_at?: string | null
+ period_starts_at?: string | null
+ price_amount?: number | null
+ product_id?: string
+ status?: Database["public"]["Enums"]["subscription_status"]
+ trial_ends_at?: string | null
+ trial_starts_at?: string | null
+ updated_at?: string
+ variant_id?: string
+ }
Relationships: [
{
- foreignKeyName: 'subscriptions_account_id_fkey';
- columns: ['account_id'];
- isOneToOne: false;
- referencedRelation: 'accounts';
- referencedColumns: ['id'];
+ foreignKeyName: "subscriptions_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "accounts"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'subscriptions_account_id_fkey';
- columns: ['account_id'];
- isOneToOne: false;
- referencedRelation: 'user_account_workspace';
- referencedColumns: ['id'];
+ foreignKeyName: "subscriptions_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "user_account_workspace"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'subscriptions_account_id_fkey';
- columns: ['account_id'];
- isOneToOne: false;
- referencedRelation: 'user_accounts';
- referencedColumns: ['id'];
+ foreignKeyName: "subscriptions_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "user_accounts"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: 'subscriptions_billing_customer_id_fkey';
- columns: ['billing_customer_id'];
- isOneToOne: false;
- referencedRelation: 'billing_customers';
- referencedColumns: ['id'];
+ foreignKeyName: "subscriptions_billing_customer_id_fkey"
+ columns: ["billing_customer_id"]
+ isOneToOne: false
+ referencedRelation: "billing_customers"
+ referencedColumns: ["id"]
},
- ];
- };
- };
+ ]
+ }
+ }
Views: {
user_account_workspace: {
Row: {
- id: string | null;
- name: string | null;
- picture_url: string | null;
+ id: string | null
+ name: string | null
+ picture_url: string | null
subscription_status:
- | Database['public']['Enums']['subscription_status']
- | null;
- };
- Relationships: [];
- };
+ | Database["public"]["Enums"]["subscription_status"]
+ | null
+ }
+ Relationships: []
+ }
user_accounts: {
Row: {
- id: string | null;
- name: string | null;
- picture_url: string | null;
- role: Database['public']['Enums']['account_role'] | null;
- slug: string | null;
- };
- Relationships: [];
- };
- };
+ id: string | null
+ name: string | null
+ picture_url: string | null
+ role: Database["public"]["Enums"]["account_role"] | null
+ slug: string | null
+ }
+ Relationships: []
+ }
+ }
Functions: {
+ accept_invitation:
+ | {
+ Args: {
+ invite_token: string
+ }
+ Returns: undefined
+ }
+ | {
+ Args: {
+ token: string
+ user_id: string
+ }
+ Returns: undefined
+ }
add_invitations_to_account: {
Args: {
- account_slug: string;
- invitations: unknown[];
- };
- Returns: Database['public']['Tables']['invitations']['Row'][];
- };
+ account_slug: string
+ invitations: unknown[]
+ }
+ Returns: Database["public"]["Tables"]["invitations"]["Row"][]
+ }
add_subscription: {
Args: {
- account_id: string;
- subscription_id: string;
- active: boolean;
- status: Database['public']['Enums']['subscription_status'];
- billing_provider: Database['public']['Enums']['billing_provider'];
- product_id: string;
- variant_id: string;
- price_amount: number;
- cancel_at_period_end: boolean;
- currency: string;
- interval: string;
- interval_count: number;
- period_starts_at: string;
- period_ends_at: string;
- trial_starts_at: string;
- trial_ends_at: string;
- customer_id: string;
- };
+ account_id: string
+ subscription_id: string
+ active: boolean
+ status: Database["public"]["Enums"]["subscription_status"]
+ billing_provider: Database["public"]["Enums"]["billing_provider"]
+ product_id: string
+ variant_id: string
+ price_amount: number
+ cancel_at_period_end: boolean
+ currency: string
+ interval: string
+ interval_count: number
+ period_starts_at: string
+ period_ends_at: string
+ trial_starts_at: string
+ trial_ends_at: string
+ customer_id: string
+ }
Returns: {
- account_id: string;
- active: boolean;
- billing_customer_id: number;
- billing_provider: Database['public']['Enums']['billing_provider'];
- cancel_at_period_end: boolean;
- created_at: string;
- currency: string;
- id: string;
- interval: string;
- interval_count: number;
- period_ends_at: string | null;
- period_starts_at: string | null;
- price_amount: number | null;
- product_id: string;
- status: Database['public']['Enums']['subscription_status'];
- trial_ends_at: string | null;
- trial_starts_at: string | null;
- updated_at: string;
- variant_id: string;
- };
- };
+ account_id: string
+ active: boolean
+ billing_customer_id: number
+ billing_provider: Database["public"]["Enums"]["billing_provider"]
+ cancel_at_period_end: boolean
+ created_at: string
+ currency: string
+ id: string
+ interval: string
+ interval_count: number
+ period_ends_at: string | null
+ period_starts_at: string | null
+ price_amount: number | null
+ product_id: string
+ status: Database["public"]["Enums"]["subscription_status"]
+ trial_ends_at: string | null
+ trial_starts_at: string | null
+ updated_at: string
+ variant_id: string
+ }
+ }
create_account: {
Args: {
- account_name: string;
- };
+ account_name: string
+ }
Returns: {
- created_at: string | null;
- created_by: string | null;
- email: string | null;
- id: string;
- is_personal_account: boolean;
- name: string;
- picture_url: string | null;
- primary_owner_user_id: string;
- slug: string | null;
- updated_at: string | null;
- updated_by: string | null;
- };
- };
+ created_at: string | null
+ created_by: string | null
+ email: string | null
+ id: string
+ is_personal_account: boolean
+ name: string
+ picture_url: string | null
+ primary_owner_user_id: string
+ slug: string | null
+ updated_at: string | null
+ updated_by: string | null
+ }
+ }
create_invitation: {
Args: {
- account_id: string;
- email: string;
- role: Database['public']['Enums']['account_role'];
- };
+ account_id: string
+ email: string
+ role: Database["public"]["Enums"]["account_role"]
+ }
Returns: {
- account_id: string;
- created_at: string;
- email: string;
- expires_at: string;
- id: number;
- invite_token: string;
- invited_by: string;
- role: Database['public']['Enums']['account_role'];
- updated_at: string;
- };
- };
+ account_id: string
+ created_at: string
+ email: string
+ expires_at: string
+ id: number
+ invite_token: string
+ invited_by: string
+ role: Database["public"]["Enums"]["account_role"]
+ updated_at: string
+ }
+ }
get_account_invitations: {
Args: {
- account_slug: string;
- };
+ account_slug: string
+ }
Returns: {
- id: number;
- email: string;
- account_id: string;
- invited_by: string;
- role: Database['public']['Enums']['account_role'];
- created_at: string;
- updated_at: string;
- inviter_name: string;
- inviter_email: string;
- }[];
- };
+ id: number
+ email: string
+ account_id: string
+ invited_by: string
+ role: Database["public"]["Enums"]["account_role"]
+ created_at: string
+ updated_at: string
+ inviter_name: string
+ inviter_email: string
+ }[]
+ }
get_account_members: {
Args: {
- account_slug: string;
- };
+ account_slug: string
+ }
Returns: {
- id: string;
- user_id: string;
- account_id: string;
- role: Database['public']['Enums']['account_role'];
- primary_owner_user_id: string;
- name: string;
- email: string;
- picture_url: string;
- created_at: string;
- updated_at: string;
- }[];
- };
+ id: string
+ user_id: string
+ account_id: string
+ role: Database["public"]["Enums"]["account_role"]
+ primary_owner_user_id: string
+ name: string
+ email: string
+ picture_url: string
+ created_at: string
+ updated_at: string
+ }[]
+ }
get_config: {
- Args: Record;
- Returns: Json;
- };
+ Args: Record
+ Returns: Json
+ }
get_user_accounts: {
- Args: Record;
+ Args: Record
Returns: {
- created_at: string | null;
- created_by: string | null;
- email: string | null;
- id: string;
- is_personal_account: boolean;
- name: string;
- picture_url: string | null;
- primary_owner_user_id: string;
- slug: string | null;
- updated_at: string | null;
- updated_by: string | null;
- }[];
- };
+ created_at: string | null
+ created_by: string | null
+ email: string | null
+ id: string
+ is_personal_account: boolean
+ name: string
+ picture_url: string | null
+ primary_owner_user_id: string
+ slug: string | null
+ updated_at: string | null
+ updated_by: string | null
+ }[]
+ }
has_permission: {
Args: {
- user_id: string;
- account_id: string;
- permission_name: Database['public']['Enums']['app_permissions'];
- };
- Returns: boolean;
- };
+ user_id: string
+ account_id: string
+ permission_name: Database["public"]["Enums"]["app_permissions"]
+ }
+ Returns: boolean
+ }
has_role_on_account: {
Args: {
- account_id: string;
- account_role?: Database['public']['Enums']['account_role'];
- };
- Returns: boolean;
- };
+ account_id: string
+ account_role?: Database["public"]["Enums"]["account_role"]
+ }
+ Returns: boolean
+ }
is_account_owner: {
Args: {
- account_id: string;
- };
- Returns: boolean;
- };
+ account_id: string
+ }
+ Returns: boolean
+ }
is_set: {
Args: {
- field_name: string;
- };
- Returns: boolean;
- };
+ field_name: string
+ }
+ Returns: boolean
+ }
is_team_member: {
Args: {
- account_id: string;
- user_id: string;
- };
- Returns: boolean;
- };
+ account_id: string
+ user_id: string
+ }
+ Returns: boolean
+ }
organization_account_workspace: {
Args: {
- account_slug: string;
- };
+ account_slug: string
+ }
Returns: {
- id: string;
- name: string;
- picture_url: string;
- slug: string;
- role: Database['public']['Enums']['account_role'];
- primary_owner_user_id: string;
- subscription_status: Database['public']['Enums']['subscription_status'];
- permissions: Database['public']['Enums']['app_permissions'][];
- }[];
- };
+ id: string
+ name: string
+ picture_url: string
+ slug: string
+ role: Database["public"]["Enums"]["account_role"]
+ primary_owner_user_id: string
+ subscription_status: Database["public"]["Enums"]["subscription_status"]
+ permissions: Database["public"]["Enums"]["app_permissions"][]
+ }[]
+ }
unaccent: {
Args: {
- '': string;
- };
- Returns: string;
- };
+ "": string
+ }
+ Returns: string
+ }
unaccent_init: {
Args: {
- '': unknown;
- };
- Returns: unknown;
- };
- };
+ "": unknown
+ }
+ Returns: unknown
+ }
+ }
Enums: {
- account_role: 'owner' | 'member';
+ account_role: "owner" | "member"
app_permissions:
- | 'roles.manage'
- | 'billing.manage'
- | 'settings.manage'
- | 'members.manage'
- | 'invites.manage';
- billing_provider: 'stripe' | 'lemon-squeezy' | 'paddle';
+ | "roles.manage"
+ | "billing.manage"
+ | "settings.manage"
+ | "members.manage"
+ | "invites.manage"
+ billing_provider: "stripe" | "lemon-squeezy" | "paddle"
subscription_status:
- | 'active'
- | 'trialing'
- | 'past_due'
- | 'canceled'
- | 'unpaid'
- | 'incomplete'
- | 'incomplete_expired'
- | 'paused';
- };
+ | "active"
+ | "trialing"
+ | "past_due"
+ | "canceled"
+ | "unpaid"
+ | "incomplete"
+ | "incomplete_expired"
+ | "paused"
+ }
CompositeTypes: {
- [_ in never]: never;
- };
- };
+ [_ in never]: never
+ }
+ }
storage: {
Tables: {
buckets: {
Row: {
- allowed_mime_types: string[] | null;
- avif_autodetection: boolean | null;
- created_at: string | null;
- file_size_limit: number | null;
- id: string;
- name: string;
- owner: string | null;
- owner_id: string | null;
- public: boolean | null;
- updated_at: string | null;
- };
+ allowed_mime_types: string[] | null
+ avif_autodetection: boolean | null
+ created_at: string | null
+ file_size_limit: number | null
+ id: string
+ name: string
+ owner: string | null
+ owner_id: string | null
+ public: boolean | null
+ updated_at: string | null
+ }
Insert: {
- allowed_mime_types?: string[] | null;
- avif_autodetection?: boolean | null;
- created_at?: string | null;
- file_size_limit?: number | null;
- id: string;
- name: string;
- owner?: string | null;
- owner_id?: string | null;
- public?: boolean | null;
- updated_at?: string | null;
- };
+ allowed_mime_types?: string[] | null
+ avif_autodetection?: boolean | null
+ created_at?: string | null
+ file_size_limit?: number | null
+ id: string
+ name: string
+ owner?: string | null
+ owner_id?: string | null
+ public?: boolean | null
+ updated_at?: string | null
+ }
Update: {
- allowed_mime_types?: string[] | null;
- avif_autodetection?: boolean | null;
- created_at?: string | null;
- file_size_limit?: number | null;
- id?: string;
- name?: string;
- owner?: string | null;
- owner_id?: string | null;
- public?: boolean | null;
- updated_at?: string | null;
- };
- Relationships: [];
- };
+ allowed_mime_types?: string[] | null
+ avif_autodetection?: boolean | null
+ created_at?: string | null
+ file_size_limit?: number | null
+ id?: string
+ name?: string
+ owner?: string | null
+ owner_id?: string | null
+ public?: boolean | null
+ updated_at?: string | null
+ }
+ Relationships: []
+ }
migrations: {
Row: {
- executed_at: string | null;
- hash: string;
- id: number;
- name: string;
- };
+ executed_at: string | null
+ hash: string
+ id: number
+ name: string
+ }
Insert: {
- executed_at?: string | null;
- hash: string;
- id: number;
- name: string;
- };
+ executed_at?: string | null
+ hash: string
+ id: number
+ name: string
+ }
Update: {
- executed_at?: string | null;
- hash?: string;
- id?: number;
- name?: string;
- };
- Relationships: [];
- };
+ executed_at?: string | null
+ hash?: string
+ id?: number
+ name?: string
+ }
+ Relationships: []
+ }
objects: {
Row: {
- bucket_id: string | null;
- created_at: string | null;
- id: string;
- last_accessed_at: string | null;
- metadata: Json | null;
- name: string | null;
- owner: string | null;
- owner_id: string | null;
- path_tokens: string[] | null;
- updated_at: string | null;
- version: string | null;
- };
+ bucket_id: string | null
+ created_at: string | null
+ id: string
+ last_accessed_at: string | null
+ metadata: Json | null
+ name: string | null
+ owner: string | null
+ owner_id: string | null
+ path_tokens: string[] | null
+ updated_at: string | null
+ version: string | null
+ }
Insert: {
- bucket_id?: string | null;
- created_at?: string | null;
- id?: string;
- last_accessed_at?: string | null;
- metadata?: Json | null;
- name?: string | null;
- owner?: string | null;
- owner_id?: string | null;
- path_tokens?: string[] | null;
- updated_at?: string | null;
- version?: string | null;
- };
+ bucket_id?: string | null
+ created_at?: string | null
+ id?: string
+ last_accessed_at?: string | null
+ metadata?: Json | null
+ name?: string | null
+ owner?: string | null
+ owner_id?: string | null
+ path_tokens?: string[] | null
+ updated_at?: string | null
+ version?: string | null
+ }
Update: {
- bucket_id?: string | null;
- created_at?: string | null;
- id?: string;
- last_accessed_at?: string | null;
- metadata?: Json | null;
- name?: string | null;
- owner?: string | null;
- owner_id?: string | null;
- path_tokens?: string[] | null;
- updated_at?: string | null;
- version?: string | null;
- };
+ bucket_id?: string | null
+ created_at?: string | null
+ id?: string
+ last_accessed_at?: string | null
+ metadata?: Json | null
+ name?: string | null
+ owner?: string | null
+ owner_id?: string | null
+ path_tokens?: string[] | null
+ updated_at?: string | null
+ version?: string | null
+ }
Relationships: [
{
- foreignKeyName: 'objects_bucketId_fkey';
- columns: ['bucket_id'];
- isOneToOne: false;
- referencedRelation: 'buckets';
- referencedColumns: ['id'];
+ foreignKeyName: "objects_bucketId_fkey"
+ columns: ["bucket_id"]
+ isOneToOne: false
+ referencedRelation: "buckets"
+ referencedColumns: ["id"]
},
- ];
- };
- };
+ ]
+ }
+ }
Views: {
- [_ in never]: never;
- };
+ [_ in never]: never
+ }
Functions: {
can_insert_object: {
Args: {
- bucketid: string;
- name: string;
- owner: string;
- metadata: Json;
- };
- Returns: undefined;
- };
+ bucketid: string
+ name: string
+ owner: string
+ metadata: Json
+ }
+ Returns: undefined
+ }
extension: {
Args: {
- name: string;
- };
- Returns: string;
- };
+ name: string
+ }
+ Returns: string
+ }
filename: {
Args: {
- name: string;
- };
- Returns: string;
- };
+ name: string
+ }
+ Returns: string
+ }
foldername: {
Args: {
- name: string;
- };
- Returns: string[];
- };
+ name: string
+ }
+ Returns: string[]
+ }
get_size_by_bucket: {
- Args: Record;
+ Args: Record
Returns: {
- size: number;
- bucket_id: string;
- }[];
- };
+ size: number
+ bucket_id: string
+ }[]
+ }
search: {
Args: {
- prefix: string;
- bucketname: string;
- limits?: number;
- levels?: number;
- offsets?: number;
- search?: string;
- sortcolumn?: string;
- sortorder?: string;
- };
+ prefix: string
+ bucketname: string
+ limits?: number
+ levels?: number
+ offsets?: number
+ search?: string
+ sortcolumn?: string
+ sortorder?: string
+ }
Returns: {
- name: string;
- id: string;
- updated_at: string;
- created_at: string;
- last_accessed_at: string;
- metadata: Json;
- }[];
- };
- };
+ name: string
+ id: string
+ updated_at: string
+ created_at: string
+ last_accessed_at: string
+ metadata: Json
+ }[]
+ }
+ }
Enums: {
- [_ in never]: never;
- };
+ [_ in never]: never
+ }
CompositeTypes: {
- [_ in never]: never;
- };
- };
-};
+ [_ in never]: never
+ }
+ }
+}
-type PublicSchema = Database[Extract];
+type PublicSchema = Database[Extract]
export type Tables<
PublicTableNameOrOptions extends
- | keyof (PublicSchema['Tables'] & PublicSchema['Views'])
+ | keyof (PublicSchema["Tables"] & PublicSchema["Views"])
| { schema: keyof Database },
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
- ? keyof (Database[PublicTableNameOrOptions['schema']]['Tables'] &
- Database[PublicTableNameOrOptions['schema']]['Views'])
+ ? keyof (Database[PublicTableNameOrOptions["schema"]]["Tables"] &
+ Database[PublicTableNameOrOptions["schema"]]["Views"])
: never = never,
> = PublicTableNameOrOptions extends { schema: keyof Database }
- ? (Database[PublicTableNameOrOptions['schema']]['Tables'] &
- Database[PublicTableNameOrOptions['schema']]['Views'])[TableName] extends {
- Row: infer R;
+ ? (Database[PublicTableNameOrOptions["schema"]]["Tables"] &
+ Database[PublicTableNameOrOptions["schema"]]["Views"])[TableName] extends {
+ Row: infer R
}
? R
: never
- : PublicTableNameOrOptions extends keyof (PublicSchema['Tables'] &
- PublicSchema['Views'])
- ? (PublicSchema['Tables'] &
- PublicSchema['Views'])[PublicTableNameOrOptions] extends {
- Row: infer R;
+ : PublicTableNameOrOptions extends keyof (PublicSchema["Tables"] &
+ PublicSchema["Views"])
+ ? (PublicSchema["Tables"] &
+ PublicSchema["Views"])[PublicTableNameOrOptions] extends {
+ Row: infer R
}
? R
: never
- : never;
+ : never
export type TablesInsert<
PublicTableNameOrOptions extends
- | keyof PublicSchema['Tables']
+ | keyof PublicSchema["Tables"]
| { schema: keyof Database },
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
- ? keyof Database[PublicTableNameOrOptions['schema']]['Tables']
+ ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"]
: never = never,
> = PublicTableNameOrOptions extends { schema: keyof Database }
- ? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends {
- Insert: infer I;
+ ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends {
+ Insert: infer I
}
? I
: never
- : PublicTableNameOrOptions extends keyof PublicSchema['Tables']
- ? PublicSchema['Tables'][PublicTableNameOrOptions] extends {
- Insert: infer I;
+ : PublicTableNameOrOptions extends keyof PublicSchema["Tables"]
+ ? PublicSchema["Tables"][PublicTableNameOrOptions] extends {
+ Insert: infer I
}
? I
: never
- : never;
+ : never
export type TablesUpdate<
PublicTableNameOrOptions extends
- | keyof PublicSchema['Tables']
+ | keyof PublicSchema["Tables"]
| { schema: keyof Database },
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
- ? keyof Database[PublicTableNameOrOptions['schema']]['Tables']
+ ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"]
: never = never,
> = PublicTableNameOrOptions extends { schema: keyof Database }
- ? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends {
- Update: infer U;
+ ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends {
+ Update: infer U
}
? U
: never
- : PublicTableNameOrOptions extends keyof PublicSchema['Tables']
- ? PublicSchema['Tables'][PublicTableNameOrOptions] extends {
- Update: infer U;
+ : PublicTableNameOrOptions extends keyof PublicSchema["Tables"]
+ ? PublicSchema["Tables"][PublicTableNameOrOptions] extends {
+ Update: infer U
}
? U
: never
- : never;
+ : never
export type Enums<
PublicEnumNameOrOptions extends
- | keyof PublicSchema['Enums']
+ | keyof PublicSchema["Enums"]
| { schema: keyof Database },
EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database }
- ? keyof Database[PublicEnumNameOrOptions['schema']]['Enums']
+ ? keyof Database[PublicEnumNameOrOptions["schema"]]["Enums"]
: never = never,
> = PublicEnumNameOrOptions extends { schema: keyof Database }
- ? Database[PublicEnumNameOrOptions['schema']]['Enums'][EnumName]
- : PublicEnumNameOrOptions extends keyof PublicSchema['Enums']
- ? PublicSchema['Enums'][PublicEnumNameOrOptions]
- : never;
+ ? Database[PublicEnumNameOrOptions["schema"]]["Enums"][EnumName]
+ : PublicEnumNameOrOptions extends keyof PublicSchema["Enums"]
+ ? PublicSchema["Enums"][PublicEnumNameOrOptions]
+ : never
+
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 62d8101ed..62d80bd35 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -119,7 +119,8 @@
"./auth-change-listener": "./src/makerkit/auth-change-listener.tsx",
"./loading-overlay": "./src/makerkit/loading-overlay.tsx",
"./profile-avatar": "./src/makerkit/profile-avatar.tsx",
- "./mdx": "./src/makerkit/mdx/mdx-renderer.tsx"
+ "./mdx": "./src/makerkit/mdx/mdx-renderer.tsx",
+ "./mode-toggle": "./src/makerkit/mode-toggle.tsx"
},
"typesVersions": {
"*": {
diff --git a/packages/ui/src/makerkit/mode-toggle.tsx b/packages/ui/src/makerkit/mode-toggle.tsx
new file mode 100644
index 000000000..5f1c876a0
--- /dev/null
+++ b/packages/ui/src/makerkit/mode-toggle.tsx
@@ -0,0 +1,99 @@
+'use client';
+
+import { useMemo } from 'react';
+
+import { Check, Moon, Sun } from 'lucide-react';
+import { useTheme } from 'next-themes';
+
+import { Button } from '../shadcn/button';
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSub,
+ DropdownMenuSubContent,
+ DropdownMenuSubTrigger,
+ DropdownMenuTrigger,
+} from '../shadcn/dropdown-menu';
+import { If } from './if';
+import { Trans } from './trans';
+
+const MODES = ['light', 'dark', 'system'];
+
+export function ModeToggle() {
+ const { setTheme } = useTheme();
+
+ const Items = useMemo(() => {
+ return MODES.map((mode) => {
+ return (
+ {
+ setTheme(mode);
+ }}
+ >
+
+
+ );
+ });
+ }, [setTheme]);
+
+ return (
+
+
+
+
+ {Items}
+
+ );
+}
+
+export function SubMenuModeToggle() {
+ const { setTheme, theme, resolvedTheme } = useTheme();
+
+ const MenuItems = useMemo(
+ () =>
+ ['light', 'dark', 'system'].map((item) => {
+ return (
+ {
+ setTheme(item);
+ }}
+ >
+
+
+
+
+
+
+ );
+ }),
+ [setTheme, theme],
+ );
+
+ return (
+
+
+
+ {resolvedTheme === 'light' ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+ {MenuItems}
+
+ );
+}
diff --git a/packages/ui/src/shadcn/separator.tsx b/packages/ui/src/shadcn/separator.tsx
index 4b86c1c29..4a584571d 100644
--- a/packages/ui/src/shadcn/separator.tsx
+++ b/packages/ui/src/shadcn/separator.tsx
@@ -4,7 +4,7 @@ import * as React from 'react';
import * as SeparatorPrimitive from '@radix-ui/react-separator';
-import { cn } from '@kit/ui/utils';
+import { cn } from '../utils';
const Separator = React.forwardRef<
React.ElementRef,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 440bebd0f..c33ae3b55 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -328,6 +328,9 @@ importers:
lucide-react:
specifier: ^0.363.0
version: 0.363.0(react@18.2.0)
+ next-themes:
+ specifier: 0.3.0
+ version: 0.3.0(react-dom@18.2.0)(react@18.2.0)
react-hook-form:
specifier: ^7.51.2
version: 7.51.2(react@18.2.0)
@@ -8843,6 +8846,16 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /next-themes@0.3.0(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==}
+ peerDependencies:
+ react: ^16.8 || ^17 || ^18
+ react-dom: ^16.8 || ^17 || ^18
+ dependencies:
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: true
+
/next@14.1.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==}
engines: {node: '>=18.17.0'}
diff --git a/supabase/migrations/20221215192558_schema.sql b/supabase/migrations/20221215192558_schema.sql
index acd37e264..a47c6f862 100644
--- a/supabase/migrations/20221215192558_schema.sql
+++ b/supabase/migrations/20221215192558_schema.sql
@@ -735,6 +735,41 @@ insert
has_role_on_account (account_id)
and public.has_permission (auth.uid (), account_id, 'invites.manage'::app_permissions));
+-- Functions
+-- Function to accept an invitation to an account
+create or replace function accept_invitation(token text, user_id uuid) returns void as $$
+declare
+ target_account_id uuid;
+ target_role public.account_role;
+begin
+ select
+ account_id,
+ role
+ into
+ target_account_id,
+ target_role
+ from
+ public.invitations
+ where
+ invite_token = token;
+
+ insert into
+ public.accounts_memberships(
+ user_id,
+ account_id,
+ account_role)
+ values
+ (accept_invitation.user_id, target_account_id, target_role);
+
+ delete from
+ public.invitations
+ where
+ invite_token = token;
+ end;
+$$ language plpgsql;
+
+grant execute on function accept_invitation (uuid) to service_role;
+
/*
* -------------------------------------------------------
* Section: Billing Customers