Files
protocol/sites/dev0x-portal/app/routes/create-account.create-team.tsx

219 lines
8.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { json } from '@remix-run/node';
import { redirect } from '@remix-run/node';
import { auth, getSignedInUser, sessionStorage } from '../auth.server';
import { z } from 'zod';
import { useState } from 'react';
import { Form, useActionData, useLoaderData, useNavigation } from '@remix-run/react';
import { TextInput } from '../components/TextInput';
import OptionSelect from '../components/OptionSelect';
import { Button } from '../components/Button';
import { validateFormData } from '../utils/utils';
import { createTeam, NO_TEAM_MARKER } from '../data/zippo.server';
import { Alert } from '../components/Alert';
import type { ActionArgs, LoaderArgs, MetaFunction } from '@remix-run/node';
import type { Option } from '../components/OptionSelect';
export const meta: MetaFunction = () => {
return {
title: 'Create Team | 0x',
description: 'Create a team on 0x',
};
};
const optionValues = z.enum(['cex', 'dex', 'self-custody-wallet', 'fintech', 'other']);
const options: Option[] = [
{ label: 'CEX', value: 'cex' },
{ label: 'DEX', value: 'dex' },
{ label: 'Self-custody Wallet', value: 'self-custody-wallet' },
{ label: 'Fintech', value: 'fintech' },
{ label: 'Other', value: 'other' },
];
const zodTeamVerifyModel = z.object({
teamName: z
.string()
.min(1, 'Please enter a valid team name')
.regex(/^[a-zA-Z0-9].*/, 'Team name must start with a letter or number'),
teamCategory: optionValues,
teamCategoryOthers: z.string().optional(),
});
type ActionInput = z.TypeOf<typeof zodTeamVerifyModel>;
type Errors = Partial<{ general: string } & Record<keyof ActionInput, string>>;
export async function loader({ request }: LoaderArgs) {
const [user, headers] = await getSignedInUser(request);
// if the user doesn't exist, we want to redirect them to the create account page
if (!user) {
throw redirect('/create-account', { headers });
}
// if the user has a team, we don't want them to be able to create a new team
if (user.teamName !== NO_TEAM_MARKER) {
throw redirect('/apps', { headers });
}
return { email: user.email };
}
export async function action({ request }: ActionArgs) {
const [user, headers] = await getSignedInUser(request);
// if the user doesn't exist, we want to redirect them to the create account page
if (!user) {
throw redirect('/create-account', { headers });
}
// if the user has a team, we don't want them to be able to create a new team
if (user.teamName !== NO_TEAM_MARKER) {
throw redirect('/apps', { headers });
}
const formData = await request.formData();
const { body, errors } = validateFormData<ActionInput>({
formData: formData,
schema: zodTeamVerifyModel,
});
if (errors || (body.teamCategory === 'other' && !body.teamCategoryOthers)) {
return json(
{
errors: {
...(errors || {}),
teamCategoryOthers:
body.teamCategory === 'other' && !body.teamCategoryOthers
? 'Please enter a valid team category'
: undefined,
} as Errors,
values: body,
},
{ headers },
);
}
const session = await sessionStorage.getSession(request.headers.get('Cookie'));
const result = await createTeam({
userId: user.id,
teamName: body.teamName,
productType: body.teamCategory === 'other' ? (body.teamCategoryOthers as string) : body.teamCategory,
});
if (result.result === 'ERROR') {
return json(
{
errors: {
general: result.error.message,
} as Errors,
},
{ headers },
);
}
const authSession = session.get(auth.sessionKey);
authSession.teamName = result.data;
session.set(auth.sessionKey, authSession);
headers.append('Set-Cookie', await sessionStorage.commitSession(session));
throw redirect('/apps', { headers });
}
function StandardCopy() {
return (
<p className="text-grey-400">
Youre creating a team on 0x. You will need to share this account with your teammates pending when
invitations are enabled.
</p>
);
}
function TeamNameInputCopy({ name }: { name: string }) {
return (
<p className="text-grey-400">
Youre creating the <strong>{name}</strong> team on 0x. You will need to share this account with your
teammates pending when invitations are enabled.
</p>
);
}
export default function CreateTeam() {
const actionData = useActionData<typeof action>();
const { email } = useLoaderData<typeof loader>();
const [teamName, setTeamName] = useState('');
const [teamType, setTeamType] = useState('');
const navigation = useNavigation();
return (
<main className="min-w-screen flex h-full min-h-full w-full flex-col bg-white">
<div className=" flex h-full w-full justify-center">
<div className="relative mt-40 inline-block w-full max-w-[456px]">
{actionData?.errors?.general && (
<Alert variant="error" className="mb-4">
{actionData?.errors?.general}
</Alert>
)}
<h1 className="text-2.5xl mb-4 text-black">Create a team</h1>
{teamName ? <TeamNameInputCopy name={teamName} /> : <StandardCopy />}
<Form method="post" className="relative mt-8 flex flex-col gap-[15px]">
<TextInput
label="Team name"
placeholder="Enter team name"
name="teamName"
value={teamName}
hiddenLabel
onChange={(val) => setTeamName(val.currentTarget.value)}
error={actionData?.errors?.teamName}
/>
<OptionSelect
options={options}
label="Team category"
hiddenLabel
name="teamCategory"
placeholder="What are you building?"
error={actionData?.errors?.teamCategory}
onChange={(val) => setTeamType(val)}
/>
{teamType === 'other' && (
<TextInput
label="What are you building (other)"
placeholder="Enter team category"
name="teamCategoryOthers"
hiddenLabel
id="teamCategoryOthers"
error={actionData?.errors?.teamCategoryOthers}
/>
)}
<Button
type="submit"
className="col-span-2 justify-center text-center"
size="md"
disabled={navigation.state !== 'idle'}
>
Finish Setup
</Button>
</Form>
</div>
</div>
<footer className="fixed bottom-0 flex w-full items-center justify-between bg-white p-8 text-center text-black">
<div className="flex flex-col items-start text-sm">
<span className="text-grey-400">Logged in as:</span>
<span>{email}</span>
</div>
<Form action="/logout" method="post">
<button type="submit" className="text-grey-800 text-base">
Log out
</button>
</Form>
</footer>
</main>
);
}