Files
protocol/sites/dev0x-portal/app/routes/_dashboard.apps.create-app._index.tsx
2023-04-06 15:37:33 +02:00

153 lines
6.3 KiB
TypeScript

import { Form, useActionData, useLoaderData, useNavigation } from '@remix-run/react';
import type { ActionArgs, LoaderArgs } from '@remix-run/server-runtime';
import { json, redirect } from '@remix-run/server-runtime';
import { useState } from 'react';
import { z } from 'zod';
import { sessionStorage } from '../auth.server';
import { BackButton } from '../components/BackButton';
import { Button } from '../components/Button';
import { MultiSelectCard } from '../components/MultiselectCard';
import { MultiSelectGroup } from '../components/MultiSelectGroup';
import { TextInput } from '../components/TextInput';
import { Book } from '../icons/Book';
import { Swap } from '../icons/Swap';
import type { CreateAppFlowType, ErrorWithGeneral } from '../types';
import { makeMultipageHandler } from '../utils/utils.server';
import { validateFormData } from '../utils/utils';
import { ArrowNarrowRight } from '../icons/ArrowNarrowRight';
const zodAppDetailsSchema = z.object({
name: z.string().min(1, 'App name is required'),
products: z
.enum(['swap-api', 'orderbook-api', 'token-registry', 'transaction-history', 'tx-relay'])
.or(
z
.array(z.enum(['swap-api', 'orderbook-api', 'token-registry', 'transaction-history', 'tx-relay']))
.min(1, 'Please select at least one product'),
),
});
type ActionInput = z.TypeOf<typeof zodAppDetailsSchema>;
type Errors = ErrorWithGeneral<Record<keyof ActionInput, string>>;
export async function action({ request }: ActionArgs) {
const formData = await request.formData();
const { body, errors } = validateFormData<ActionInput>({
formData: formData,
schema: zodAppDetailsSchema,
});
if (errors) {
return json({ errors: errors as Errors, values: body });
}
const session = await sessionStorage.getSession(request.headers.get('Cookie'));
const sessionHandler = makeMultipageHandler<CreateAppFlowType>({ session, namespace: 'create-app' });
sessionHandler.setPage(0, {
appName: body.name,
products: Array.isArray(body.products) ? body.products : [body.products],
});
const header = await sessionStorage.commitSession(session);
throw redirect('/apps/create-app/explorer-tag', { headers: { 'Set-Cookie': header } });
}
export async function loader({ request }: LoaderArgs) {
const session = await sessionStorage.getSession(request.headers.get('Cookie'));
const sessionHandler = makeMultipageHandler<CreateAppFlowType>({ session, namespace: 'create-app' });
const currentData = sessionHandler.getPage(0);
const lastPage = sessionHandler.getPage(2);
if (lastPage) {
throw redirect('/apps/create-app/api-key');
}
return json({ data: currentData || null });
}
export default function AppDetails() {
const { data } = useLoaderData<typeof loader>();
const actionData = useActionData<typeof action>();
const navigation = useNavigation();
const [appName, setAppName] = useState(actionData?.values.name || data?.appName || '');
const [selectedProducts, setSelectedProducts] = useState(
new Set(actionData?.values.products || data?.products || ['swap-api', 'orderbook-api']),
);
return (
<div className="flex h-full flex-col pt-8">
<div className="px-10 ">
<BackButton />
<h2 className="text-2.5xl text-grey-900 font-sans leading-[120%] antialiased">App Details</h2>
<hr className="text-grey-200 mt-4 mb-[52px]" />
</div>
<Form method="post" className="flex flex-grow flex-col justify-between">
<div className="px-10">
<TextInput
label="App Name"
name="name"
placeholder="Enter app name"
id="inp-app-name"
className="mb-10"
onChange={(e) => setAppName(e.target.value)}
error={actionData?.errors?.name}
initialValue={appName}
/>
<MultiSelectGroup
label="What 0x products should be enabled?"
onChange={(e) => {
const { value, checked } = e.target as HTMLInputElement;
if (checked) {
selectedProducts.add(value);
} else {
selectedProducts.delete(value);
}
setSelectedProducts(new Set(selectedProducts));
}}
>
<MultiSelectCard
title="Swap API"
description="Access efficient liquidity for powering token swaps"
icon={<Swap />}
id="swap-api"
selected={selectedProducts.has('swap-api')}
key="swap-api"
className="h-full"
name="products"
value="swap-api"
/>
<MultiSelectCard
title="Orderbook API"
description="Power limit orders in your application"
icon={<Book />}
id="orderbook-api"
selected={selectedProducts.has('orderbook-api')}
key="orderbook-api"
className="h-full"
name="products"
value="orderbook-api"
/>
</MultiSelectGroup>
</div>
<div className="flex-grow" />
<div className="p-6">
<Button
size="md"
className="w-full justify-center"
type="submit"
disabled={navigation.state !== 'idle' || appName === '' || selectedProducts.size === 0}
>
Continue
<ArrowNarrowRight height={24} width={24} className="ml-2" />
</Button>
</div>
</Form>
</div>
);
}