Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/dashboard/src/@/components/ui/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background selection:bg-foreground selection:text-background file:border-0 file:bg-transparent file:font-medium file:text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background selection:bg-foreground/10 file:border-0 file:bg-transparent file:font-medium file:text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className,
)}
ref={ref}
Expand Down
20 changes: 20 additions & 0 deletions apps/dashboard/src/@/components/ui/tracked-link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useTrack } from "hooks/analytics/useTrack";
import Link from "next/link";
import type React from "react";
import { cn } from "../../lib/utils";

export type TrackedLinkProps = React.ComponentProps<typeof Link> & {
category: string;
Expand All @@ -24,3 +25,22 @@ export function TrackedLinkTW(props: TrackedLinkProps) {
/>
);
}

export function TrackedUnderlineLink(props: TrackedLinkProps) {
const trackEvent = useTrack();
const { category, label, trackingProps, ...restProps } = props;

return (
<Link
{...restProps}
onClick={(e) => {
trackEvent({ category, action: "click", label, ...trackingProps });
props.onClick?.(e);
}}
className={cn(
"underline decoration-muted-foreground/50 decoration-dotted underline-offset-[5px] hover:text-foreground hover:decoration-foreground hover:decoration-solid",
restProps.className,
)}
/>
);
}
2 changes: 1 addition & 1 deletion apps/dashboard/src/@/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
--link-foreground: 221.21deg 83.19% 53.33%;
--success-text: 142.09 70.56% 35.29%;
--warning-text: 38 92% 40%;
--destructive-text: 357.15deg 100% 68.72%;
--destructive-text: 360 72% 60%;

/* Borders */
--border: 0 0% 85%;
Expand Down
128 changes: 49 additions & 79 deletions apps/dashboard/src/@3rdweb-sdk/react/hooks/useEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,101 +230,71 @@ export function useEngineUpdateDeployment() {
});
}

export function useEngineRemoveFromDashboard() {
const address = useActiveAccount()?.address;
const queryClient = useQueryClient();

return useMutation({
mutationFn: async (instanceId: string) => {
invariant(instanceId, "instance is required");

const res = await apiServerProxy({
pathname: `/v1/engine/${instanceId}`,
method: "DELETE",
});

if (!res.ok) {
throw new Error(res.error);
}
},
export type RemoveEngineFromDashboardIParams = {
instanceId: string;
};

onSuccess: () => {
return queryClient.invalidateQueries({
queryKey: engineKeys.instances(address || ""),
});
},
export async function removeEngineFromDashboard({
instanceId,
}: RemoveEngineFromDashboardIParams) {
const res = await apiServerProxy({
pathname: `/v1/engine/${instanceId}`,
method: "DELETE",
});

if (!res.ok) {
throw new Error(res.error);
}
}

export interface DeleteCloudHostedInput {
export type DeleteCloudHostedEngineParams = {
deploymentId: string;
reason: "USING_SELF_HOSTED" | "TOO_EXPENSIVE" | "MISSING_FEATURES" | "OTHER";
feedback: string;
}

export function useEngineDeleteCloudHosted() {
const address = useActiveAccount()?.address;
const queryClient = useQueryClient();

return useMutation({
mutationFn: async ({
deploymentId,
reason,
feedback,
}: DeleteCloudHostedInput) => {
const res = await apiServerProxy({
pathname: `/v2/engine/deployments/${deploymentId}/infrastructure/delete`,
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ reason, feedback }),
});

if (!res.ok) {
throw new Error(res.error);
}
},
};

onSuccess: () => {
return queryClient.invalidateQueries({
queryKey: engineKeys.instances(address || ""),
});
},
export async function deleteCloudHostedEngine({
deploymentId,
reason,
feedback,
}: DeleteCloudHostedEngineParams) {
const res = await apiServerProxy({
pathname: `/v2/engine/deployments/${deploymentId}/infrastructure/delete`,
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ reason, feedback }),
});

if (!res.ok) {
throw new Error(res.error);
}
}

export interface EditEngineInstanceInput {
export type EditEngineInstanceParams = {
instanceId: string;
name: string;
url: string;
}

export function useEngineEditInstance() {
const address = useActiveAccount()?.address;
const queryClient = useQueryClient();

return useMutation({
mutationFn: async ({ instanceId, name, url }: EditEngineInstanceInput) => {
const res = await apiServerProxy({
pathname: `/v1/engine/${instanceId}`,
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ name, url }),
});
};

if (!res.ok) {
throw new Error(res.error);
}
},
onSuccess: () => {
return queryClient.invalidateQueries({
queryKey: engineKeys.instances(address || ""),
});
},
export async function editEngineInstance({
instanceId,
name,
url,
}: EditEngineInstanceParams) {
const res = await apiServerProxy({
pathname: `/v1/engine/${instanceId}`,
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ name, url }),
});

if (!res.ok) {
throw new Error(res.error);
}
}

export type Transaction = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { SidebarLayout } from "@/components/blocks/SidebarLayout";
import {} from "@/constants/cookie";
import { redirect } from "next/navigation";
import { getAuthToken } from "../../../../../../../../api/lib/getAuthToken";
import { fetchEcosystem } from "../../../utils/fetchEcosystem";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Button } from "@/components/ui/button";
import {} from "@/constants/cookie";
import { ArrowRightIcon, ExternalLinkIcon } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"use client";

import { Button } from "@/components/ui/button";
import {
ArrowRightIcon,
DownloadIcon,
ExternalLinkIcon,
PlusIcon,
} from "lucide-react";
import Link from "next/link";
import { useTrack } from "../../../../../../../hooks/analytics/useTrack";

export function CreateEngineLink(props: {
label: string;
engineLinkPrefix: string;
}) {
const trackEvent = useTrack();

return (
<Button
asChild
variant="default"
size="sm"
onClick={() => {
trackEvent({
category: "engine",
action: "click",
label: "add-engine-instance",
});
}}
>
<Link href={`${props.engineLinkPrefix}/create`} className="gap-2">
<PlusIcon className="size-3" />
{props.label}
</Link>
</Button>
);
}

export function ImportEngineLink(props: {
label: string;
engineLinkPrefix: string;
}) {
const trackEvent = useTrack();

return (
<Button
asChild
variant="outline"
size="sm"
className="gap-2 bg-card"
onClick={() => {
trackEvent({
category: "engine",
action: "import",
});
}}
>
<Link href={`${props.engineLinkPrefix}/import`}>
<DownloadIcon className="size-3" />
{props.label}
</Link>
</Button>
);
}

export function EngineInfoCard(props: { team_slug: string }) {
const engineLinkPrefix = `/team/${props.team_slug}/~/engine`;
const trackEvent = useTrack();

return (
<div className=" rounded-lg border border-border bg-card">
<div className="p-6">
<h1 className="font-semibold text-xl tracking-tight">
Your scalable web3 backend server
</h1>

<div className="h-2" />

<ul className="list-disc space-y-2 pl-3 text-muted-foreground text-sm">
<li>Read, write, and deploy contracts at production scale</li>
<li>
Reliably parallelize and retry transactions with gas & nonce
management
</li>
<li>Securely manage backend wallets</li>
<li>Built-in support for account abstraction, relayers, and more</li>
</ul>
</div>

<div className="flex justify-end gap-3 border-border border-t p-4 lg:px-6">
<Button asChild variant="outline" size="sm">
<Link
href="https://portal.thirdweb.com/engine"
className="gap-2"
target="_blank"
>
Learn More
<ExternalLinkIcon className="size-3 text-muted-foreground" />
</Link>
</Button>

<Button
size="sm"
asChild
onClick={() => {
trackEvent({
category: "engine",
action: "try-demo",
label: "clicked-try-demo",
});
}}
variant="outline"
>
<Link href={`${engineLinkPrefix}/sandbox`} className="gap-2">
Try Demo Engine
<ArrowRightIcon className="size-3 text-muted-foreground" />
</Link>
</Button>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import { EngineTierCard } from "./tier-card";

export default function Page() {
return (
<div>
<h1 className="mb-2 font-semibold text-2xl tracking-tight">
<div className="pb-20">
<h1 className="mb-1 font-semibold text-2xl tracking-tight">
Choose an Engine deployment
</h1>

<p className="mb-7 text-muted-foreground">
Host Engine on thirdweb with no setup or maintenance required.
<p className="mb-8 text-muted-foreground text-sm">
Host Engine on thirdweb with no setup or maintenance required
</p>

<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
<div className="grid grid-cols-1 gap-4 lg:grid-cols-3">
<EngineTierCard tier="STARTER" />
<EngineTierCard tier="PREMIUM" isPrimaryCta />
<EngineTierCard tier="ENTERPRISE" previousTier="Premium Engine" />
Expand Down
Loading
Loading