Skip to content

Commit b742c80

Browse files
committed
Dashboard: Migrate engine/configuration page from chakra to tailwind
1 parent a252ceb commit b742c80

File tree

9 files changed

+480
-439
lines changed

9 files changed

+480
-439
lines changed

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/configuration/components/circle-config.tsx

Lines changed: 52 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Link from "next/link";
1+
import { SaveIcon } from "lucide-react";
22
import { useId } from "react";
33
import { useForm } from "react-hook-form";
44
import { toast } from "sonner";
@@ -7,6 +7,7 @@ import { Button } from "@/components/ui/button";
77
import { Form } from "@/components/ui/form";
88
import { Input } from "@/components/ui/input";
99
import { Spinner } from "@/components/ui/Spinner/Spinner";
10+
import { UnderlineLink } from "@/components/ui/UnderlineLink";
1011
import {
1112
type EngineInstance,
1213
type SetWalletConfigInput,
@@ -58,55 +59,61 @@ export const CircleConfig: React.FC<CircleConfigProps> = ({
5859
const circleApiKeyId = useId();
5960

6061
return (
61-
<div className="flex flex-col gap-6">
62-
<div className="flex flex-col gap-2">
63-
<p className="text-muted-foreground">
64-
Circle wallets require an API Key from your Circle account with
65-
sufficient permissions. Created wallets are stored in your AWS
66-
account. Configure your Circle API Key to use Circle wallets. Learn
67-
more about{" "}
68-
<Link
69-
className="text-link-foreground hover:text-foreground"
70-
href="https://portal.thirdweb.com/engine/features/backend-wallets#circle-wallet"
71-
rel="noopener noreferrer"
72-
target="_blank"
73-
>
74-
how to get an API Key
75-
</Link>
76-
.
77-
</p>
78-
</div>
79-
62+
<div className="bg-card rounded-lg border mb-8">
8063
<Form {...form}>
81-
<form
82-
className="flex flex-col gap-4"
83-
onSubmit={form.handleSubmit(onSubmit)}
84-
>
85-
<FormFieldSetup
86-
errorMessage={
87-
form.getFieldState("circleApiKey", form.formState).error?.message
88-
}
89-
htmlFor={circleApiKeyId}
90-
isRequired
91-
label="Circle API Key"
92-
tooltip={null}
93-
>
94-
<Input
95-
autoComplete="off"
96-
id={circleApiKeyId}
97-
placeholder="TEST_API_KEY:..."
98-
type="password"
99-
{...form.register("circleApiKey")}
100-
/>
101-
</FormFieldSetup>
64+
<form onSubmit={form.handleSubmit(onSubmit)}>
65+
<div className="p-4 lg:p-6">
66+
<div className="mb-4">
67+
<h2 className="text-lg font-semibold mb-1">Credentials</h2>
68+
69+
<p className="text-muted-foreground text-sm">
70+
Circle wallets require an API Key from your Circle account with
71+
sufficient permissions. <br /> Created wallets are stored in
72+
your AWS account. Configure your Circle API Key to use Circle
73+
wallets. Learn more about{" "}
74+
<UnderlineLink
75+
href="https://portal.thirdweb.com/engine/features/backend-wallets#circle-wallet"
76+
rel="noopener noreferrer"
77+
target="_blank"
78+
>
79+
how to get an API Key
80+
</UnderlineLink>
81+
.
82+
</p>
83+
</div>
84+
85+
<FormFieldSetup
86+
errorMessage={
87+
form.getFieldState("circleApiKey", form.formState).error
88+
?.message
89+
}
90+
htmlFor={circleApiKeyId}
91+
isRequired
92+
label="Circle API Key"
93+
tooltip={null}
94+
>
95+
<Input
96+
autoComplete="off"
97+
id={circleApiKeyId}
98+
placeholder="TEST_API_KEY:..."
99+
type="password"
100+
{...form.register("circleApiKey")}
101+
/>
102+
</FormFieldSetup>
103+
</div>
102104

103-
<div className="flex items-center justify-end gap-4">
105+
<div className="flex items-center justify-end gap-4 border-t border-dashed px-4 py-4 lg:px-6">
104106
<Button
105-
className="min-w-28 gap-2"
106-
disabled={isPending}
107+
className="gap-2"
108+
disabled={isPending || !form.formState.isDirty}
109+
size="sm"
107110
type="submit"
108111
>
109-
{isPending && <Spinner className="size-4" />}
112+
{isPending ? (
113+
<Spinner className="size-4" />
114+
) : (
115+
<SaveIcon className="size-4" />
116+
)}
110117
Save
111118
</Button>
112119
</div>

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/configuration/components/cors.tsx

Lines changed: 51 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,36 @@
1-
import { Flex, Textarea } from "@chakra-ui/react";
2-
import { Button } from "chakra/button";
3-
import { Heading } from "chakra/heading";
4-
import { Text } from "chakra/text";
1+
import { SaveIcon } from "lucide-react";
52
import { useForm } from "react-hook-form";
3+
import { toast } from "sonner";
4+
import { Button } from "@/components/ui/button";
65
import { InlineCode } from "@/components/ui/inline-code";
6+
import { Spinner } from "@/components/ui/Spinner/Spinner";
7+
import { Textarea } from "@/components/ui/textarea";
78
import {
89
useEngineCorsConfiguration,
910
useEngineSetCorsConfiguration,
1011
} from "@/hooks/useEngine";
11-
import { useTxNotifications } from "@/hooks/useTxNotifications";
12+
import { parseError } from "@/utils/errorParser";
1213

13-
interface EngineCorsConfigProps {
14-
instanceUrl: string;
15-
authToken: string;
16-
}
17-
18-
interface CorsForm {
14+
type CorsForm = {
1915
raw: string;
20-
}
16+
};
2117

22-
export const EngineCorsConfig: React.FC<EngineCorsConfigProps> = ({
18+
export function EngineCorsConfig({
2319
instanceUrl,
2420
authToken,
25-
}) => {
21+
}: {
22+
instanceUrl: string;
23+
authToken: string;
24+
}) {
2625
const { data: existingUrls } = useEngineCorsConfiguration({
2726
authToken,
2827
instanceUrl,
2928
});
30-
const { mutateAsync: setCorsConfig } = useEngineSetCorsConfiguration({
29+
const setCorsConfigMutation = useEngineSetCorsConfiguration({
3130
authToken,
3231
instanceUrl,
3332
});
3433

35-
const { onSuccess, onError } = useTxNotifications(
36-
"CORS URLs updated successfully.",
37-
"Failed to update CORS URLs.",
38-
);
39-
4034
const form = useForm<CorsForm>({
4135
values: { raw: existingUrls?.join("\n") ?? "" },
4236
});
@@ -47,52 +41,57 @@ export const EngineCorsConfig: React.FC<EngineCorsConfigProps> = ({
4741
// Assert all URLs are well formed and strip the path.
4842
const sanitized = urls.map(parseOriginFromUrl);
4943

50-
await setCorsConfig({ urls: sanitized });
51-
onSuccess();
44+
await setCorsConfigMutation.mutateAsync({ urls: sanitized });
45+
toast.success("CORS URLs updated successfully.");
5246
} catch (error) {
53-
onError(error);
47+
toast.error("Failed to update CORS URLs.", {
48+
description: parseError(error),
49+
});
5450
}
5551
};
5652

5753
return (
58-
<Flex
59-
as="form"
60-
flexDir="column"
61-
gap={4}
62-
onSubmit={form.handleSubmit(onSubmit)}
63-
>
64-
<Flex flexDir="column" gap={2}>
65-
<Heading size="title.md">Allowlisted Domains</Heading>
66-
<Text>
54+
<div className="bg-card rounded-lg border">
55+
<div className="px-4 lg:px-6 pt-4 lg:pt-6">
56+
<h2 className="text-lg font-semibold mb-1">Allowlisted Domains</h2>
57+
<p className="text-sm text-muted-foreground">
6758
Specify the origins that can call Engine (
6859
<InlineCode code="https://example.com" />
6960
).
7061
<br />
7162
Enter one origin per line, or leave this list empty to disallow calls
7263
from web clients.
73-
</Text>
74-
</Flex>
64+
</p>
65+
</div>
7566

76-
<Textarea
77-
placeholder={"https://example.com\nhttp://localhost:3000"}
78-
rows={4}
79-
{...form.register("raw")}
80-
/>
67+
<form onSubmit={form.handleSubmit(onSubmit)}>
68+
<div className="p-4 lg:px-6">
69+
<Textarea
70+
placeholder={"https://example.com\nhttp://localhost:3000"}
71+
rows={4}
72+
{...form.register("raw")}
73+
/>
74+
</div>
8175

82-
<Flex alignItems="center" gap={4} justifyContent="end">
83-
<Button
84-
colorScheme="primary"
85-
isDisabled={!form.formState.isDirty}
86-
px={12}
87-
type="submit"
88-
w={{ base: "full", md: "inherit" }}
89-
>
90-
{form.formState.isSubmitting ? "Saving..." : "Save"}
91-
</Button>
92-
</Flex>
93-
</Flex>
76+
<div className="flex justify-end border-t border-dashed px-4 py-4 lg:px-6">
77+
<Button
78+
className="gap-2"
79+
size="sm"
80+
type="submit"
81+
disabled={!form.formState.isDirty || form.formState.isSubmitting}
82+
>
83+
{setCorsConfigMutation.isPending ? (
84+
<Spinner className="size-4" />
85+
) : (
86+
<SaveIcon className="size-4" />
87+
)}
88+
Save
89+
</Button>
90+
</div>
91+
</form>
92+
</div>
9493
);
95-
};
94+
}
9695

9796
const parseOriginFromUrl = (url: string) => {
9897
try {

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/configuration/components/engine-configuration.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,25 @@ export const EngineConfiguration: React.FC<EngineConfigurationProps> = ({
2020
authToken,
2121
}) => {
2222
return (
23-
<div className="flex flex-col gap-12">
23+
<div>
2424
<EngineWalletConfig
2525
authToken={authToken}
2626
instance={instance}
2727
projectSlug={projectSlug}
2828
teamSlug={teamSlug}
2929
/>
30-
<EngineCorsConfig authToken={authToken} instanceUrl={instance.url} />
31-
<EngineIpAllowlistConfig
32-
authToken={authToken}
33-
instanceUrl={instance.url}
34-
/>
35-
<EngineSystem
36-
instance={instance}
37-
projectSlug={projectSlug}
38-
teamIdOrSlug={teamSlug}
39-
/>
30+
<div className="space-y-8">
31+
<EngineCorsConfig authToken={authToken} instanceUrl={instance.url} />
32+
<EngineIpAllowlistConfig
33+
authToken={authToken}
34+
instanceUrl={instance.url}
35+
/>
36+
<EngineSystem
37+
instance={instance}
38+
projectSlug={projectSlug}
39+
teamIdOrSlug={teamSlug}
40+
/>
41+
</div>
4042
</div>
4143
);
4244
};

0 commit comments

Comments
 (0)