B2B-30: add button to create company from super admin page
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useTransition } from 'react';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from '@kit/ui/alert-dialog';
|
||||
import { Button } from '@kit/ui/button';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@kit/ui/form';
|
||||
import { If } from '@kit/ui/if';
|
||||
import { Input } from '@kit/ui/input';
|
||||
import { toast } from '@kit/ui/sonner';
|
||||
|
||||
import { createTeamAccountAction } from '../lib/server/admin-server-actions';
|
||||
import { CreateCompanySchema, CreateCompanySchemaType } from '../lib/server/schema/create-company.schema';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
export function AdminCreateCompanyDialog(props: React.PropsWithChildren) {
|
||||
const [pending, startTransition] = useTransition();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const form = useForm<CreateCompanySchemaType>({
|
||||
resolver: zodResolver(CreateCompanySchema),
|
||||
defaultValues: {
|
||||
name: '',
|
||||
},
|
||||
mode: 'onChange',
|
||||
});
|
||||
|
||||
const onSubmit = (data: CreateCompanySchemaType) => {
|
||||
startTransition(async () => {
|
||||
try {
|
||||
const error = await createTeamAccountAction(data);
|
||||
|
||||
if (!error) {
|
||||
toast.success('Company creates successfully');
|
||||
form.reset();
|
||||
|
||||
setOpen(false);
|
||||
setError(null);
|
||||
|
||||
|
||||
} else {
|
||||
setError('Something went wrong with company creation');
|
||||
|
||||
}
|
||||
|
||||
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : 'Error');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<AlertDialog open={open} onOpenChange={setOpen}>
|
||||
<AlertDialogTrigger asChild>{props.children}</AlertDialogTrigger>
|
||||
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Create New Company Account</AlertDialogTitle>
|
||||
|
||||
<AlertDialogDescription>
|
||||
Complete the form below to create a new company account.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
|
||||
<Form {...form}>
|
||||
<form
|
||||
data-test={'admin-create-user-form'}
|
||||
className={'flex flex-col space-y-4'}
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
>
|
||||
<If condition={!!error}>
|
||||
<Alert variant={'destructive'}>
|
||||
<AlertTitle>Error</AlertTitle>
|
||||
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
</Alert>
|
||||
</If>
|
||||
|
||||
<FormField
|
||||
name={'name'}
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'teams:teamNameLabel'} />
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
<Input
|
||||
data-test={'create-team-name-input'}
|
||||
required
|
||||
minLength={2}
|
||||
maxLength={50}
|
||||
placeholder={''}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>
|
||||
<Trans i18nKey={'teams:teamNameDescription'} />
|
||||
</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
|
||||
<Button disabled={pending} type={'submit'}>
|
||||
{pending ? 'Creating...' : 'Create Company'}
|
||||
</Button>
|
||||
</AlertDialogFooter>
|
||||
</form>
|
||||
</Form>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
@@ -20,6 +20,8 @@ import { ResetPasswordSchema } from './schema/reset-password.schema';
|
||||
import { createAdminAccountsService } from './services/admin-accounts.service';
|
||||
import { createAdminAuthUserService } from './services/admin-auth-user.service';
|
||||
import { adminAction } from './utils/admin-action';
|
||||
import { CreateCompanySchema } from './schema/create-company.schema';
|
||||
import { createCreateTeamAccountService } from './services/create-team-account.service';
|
||||
|
||||
/**
|
||||
* @name banUserAction
|
||||
@@ -222,6 +224,42 @@ export const resetPasswordAction = adminAction(
|
||||
),
|
||||
);
|
||||
|
||||
export const createTeamAccountAction = enhanceAction(
|
||||
async ({ name }, user) => {
|
||||
const logger = await getLogger();
|
||||
const client = getSupabaseServerClient();
|
||||
const service = createCreateTeamAccountService(client);
|
||||
|
||||
const ctx = {
|
||||
name: 'team-accounts.create',
|
||||
userId: user.id,
|
||||
accountName: name,
|
||||
};
|
||||
|
||||
logger.info(ctx, `Creating company account...`);
|
||||
|
||||
const { data, error } = await service.createNewOrganizationAccount({
|
||||
name,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
logger.error({ ...ctx, error }, `Failed to create company account`);
|
||||
|
||||
return {
|
||||
error: true,
|
||||
};
|
||||
}
|
||||
|
||||
logger.info(ctx, `Company account created`);
|
||||
|
||||
redirect(`/home/${data.slug}/settings`);
|
||||
},
|
||||
{
|
||||
schema: CreateCompanySchema,
|
||||
},
|
||||
);
|
||||
|
||||
function revalidateAdmin() {
|
||||
revalidatePath('/admin', 'layout');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* @name RESERVED_NAMES_ARRAY
|
||||
* @description Array of reserved names for team accounts
|
||||
* This is a list of names that cannot be used for team accounts as they are reserved for other purposes.
|
||||
* Please include any new reserved names here.
|
||||
*/
|
||||
const RESERVED_NAMES_ARRAY = [
|
||||
'settings',
|
||||
'billing',
|
||||
// please add more reserved names here
|
||||
];
|
||||
|
||||
const SPECIAL_CHARACTERS_REGEX = /[!@#$%^&*()+=[\]{};':"\\|,.<>/?]/;
|
||||
|
||||
/**
|
||||
* @name CompanyNameSchema
|
||||
*/
|
||||
export const CompanyNameSchema = z
|
||||
.string({
|
||||
description: 'The name of the company account',
|
||||
})
|
||||
.min(2)
|
||||
.max(50)
|
||||
.refine(
|
||||
(name) => {
|
||||
console.log(name);
|
||||
return !SPECIAL_CHARACTERS_REGEX.test(name);
|
||||
},
|
||||
{
|
||||
message: 'teams:specialCharactersError',
|
||||
},
|
||||
)
|
||||
.refine(
|
||||
(name) => {
|
||||
return !RESERVED_NAMES_ARRAY.includes(name.toLowerCase());
|
||||
},
|
||||
{
|
||||
message: 'teams:reservedNameError',
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @name CreateCompanySchema
|
||||
* @description Schema for creating a team account
|
||||
*/
|
||||
export const CreateCompanySchema = z.object({
|
||||
name: CompanyNameSchema,
|
||||
});
|
||||
|
||||
export type CreateCompanySchemaType = z.infer<typeof CreateCompanySchema>;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import 'server-only';
|
||||
|
||||
import { SupabaseClient } from '@supabase/supabase-js';
|
||||
|
||||
import { getLogger } from '@kit/shared/logger';
|
||||
import { Database } from '@kit/supabase/database';
|
||||
|
||||
export function createCreateTeamAccountService(
|
||||
client: SupabaseClient<Database>,
|
||||
) {
|
||||
return new CreateTeamAccountService(client);
|
||||
}
|
||||
|
||||
class CreateTeamAccountService {
|
||||
private readonly namespace = 'accounts.create-team-account';
|
||||
|
||||
constructor(private readonly client: SupabaseClient<Database>) {}
|
||||
|
||||
async createNewOrganizationAccount(params: { name: string; userId: string }) {
|
||||
const logger = await getLogger();
|
||||
const ctx = { ...params, namespace: this.namespace };
|
||||
|
||||
logger.info(ctx, `Creating new company account...`);
|
||||
|
||||
const { error, data } = await this.client.rpc('create_team_account', {
|
||||
account_name: params.name,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
logger.error(
|
||||
{
|
||||
error,
|
||||
...ctx,
|
||||
},
|
||||
`Error creating company account`,
|
||||
);
|
||||
|
||||
throw new Error('Error creating company account');
|
||||
}
|
||||
|
||||
logger.info(ctx, `Company account created successfully`);
|
||||
|
||||
return { data, error };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user