update routing
This commit is contained in:
51
src/api/api.ts
Normal file
51
src/api/api.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
const _apiRoot = "https://ai-api.cytonic.com";
|
||||
export class ApiError extends Error {
|
||||
constructor(description: string, error: string, trace_id: string) {
|
||||
super(error);
|
||||
this.name = "ApiError";
|
||||
this.message = description;
|
||||
this.stack = trace_id;
|
||||
this.cause = error;
|
||||
}
|
||||
}
|
||||
|
||||
async function request(
|
||||
entrypoint: string,
|
||||
method: "POST" | "GET" = "GET",
|
||||
body?: object,
|
||||
) {
|
||||
const apiRoot = localStorage.getItem("API_ROOT") || _apiRoot;
|
||||
const address =
|
||||
apiRoot + (entrypoint[0] === "/" ? entrypoint : `/${entrypoint}`);
|
||||
|
||||
const res = await fetch(address, {
|
||||
method,
|
||||
body: JSON.stringify(body),
|
||||
headers: {
|
||||
...(body && { "Content-Type": "application/json" }),
|
||||
},
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json();
|
||||
throw new ApiError(error.description, error.error, error.trace_id);
|
||||
}
|
||||
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
const api = {
|
||||
get(
|
||||
entrypoint: string,
|
||||
queryObj?: string | URLSearchParams | Record<string, string> | string[][],
|
||||
) {
|
||||
let queryStr = "";
|
||||
if (queryObj) queryStr = `?${new URLSearchParams(queryObj).toString()}`;
|
||||
return request(entrypoint + queryStr);
|
||||
},
|
||||
post(entrypoint: string, body?: object) {
|
||||
return request(entrypoint, "POST", body);
|
||||
},
|
||||
};
|
||||
|
||||
export { api };
|
||||
@@ -2,77 +2,19 @@ import { observer } from "mobx-react-lite";
|
||||
import { useRef, useState } from "react";
|
||||
import Arrow from "@/icons/arrow.svg?react";
|
||||
import Paperclip from "@/icons/paperclip.svg?react";
|
||||
import Clock from "@/icons/clock.svg?react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import { useNavigate, useParams } from "@tanstack/react-router";
|
||||
import { useAccountStore } from "@/contexts/AccountContext";
|
||||
import { Button } from "../ui/button";
|
||||
import { useToast } from "@/lib/hooks/use-toast";
|
||||
|
||||
const Chat = observer(() => {
|
||||
const { id } = useParams({ from: "/interaction-panel/$id" });
|
||||
return (
|
||||
<div className="flex-1 h-full flex items-center justify-center">
|
||||
<WelcomePanel />
|
||||
</div>
|
||||
<div className="flex-1 h-full flex items-center justify-center">{id}</div>
|
||||
);
|
||||
});
|
||||
|
||||
const WelcomePanel = () => {
|
||||
const navigate = useNavigate();
|
||||
const { userId } = useAccountStore();
|
||||
const { toast } = useToast();
|
||||
|
||||
return (
|
||||
<div className="max-w-[741px] flex flex-col gap-8">
|
||||
<div className="flex flex-col gap-2 font-medium">
|
||||
<span className="leading-[120%] text-black text-[24px]">
|
||||
Hi!
|
||||
<br /> I will help you create a crypto project on Cytonic
|
||||
</span>
|
||||
<span className="text-[#C0C0C0] text-[14px] flex gap-2 items-center">
|
||||
<Clock />
|
||||
<p>Estimated build time: 3 minutes</p>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<p className="text-[#C0C0C0] text-[14px] font-medium leading-[120%]">
|
||||
Choose one of the most common prompts
|
||||
<br /> below or use your own to begin
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
{[
|
||||
{ emoji: "🐶", text: "Build a memecoin launchpad" },
|
||||
{ emoji: "🏦", text: "Build a new stablecoin protocol" },
|
||||
{ emoji: "🌐", text: "Create a RWA marketplace on Cytonic" },
|
||||
{ emoji: "📊", text: "Ship a token dashboard" },
|
||||
].map(({ emoji, text }) => (
|
||||
<button
|
||||
type="button"
|
||||
key={emoji}
|
||||
onClick={() => {
|
||||
if (userId === undefined) {
|
||||
navigate({
|
||||
to: "/auth/mail",
|
||||
viewTransition: { types: ["warp"] },
|
||||
});
|
||||
}
|
||||
}}
|
||||
className="text-left cursor-pointer hover:bg-fill-100 transition-all w-[173px] h-[123px] p-4 rounded-[16px] border border-[#E8E8E8] flex flex-col justify-between"
|
||||
>
|
||||
<p className="text-[14px] font-medium leading-[120%]">{text}</p>
|
||||
<div className="text-[24px] leading-[100%]">{emoji}</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<ChatInput />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ChatInput = () => {
|
||||
const navigate = useNavigate();
|
||||
const { userId } = useAccountStore();
|
||||
@@ -156,4 +98,4 @@ const ChatInput = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export { Chat, WelcomePanel, ChatInput };
|
||||
export { Chat, ChatInput };
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -4,11 +4,12 @@ import Plus from "@/icons/plus.svg?react";
|
||||
import Books from "@/icons/books.svg?react";
|
||||
import Discord from "@/icons/discord.svg?react";
|
||||
import Profile from "@/icons/profile.svg?react";
|
||||
import ThreeDots from "@/icons/three-dots.svg?react";
|
||||
|
||||
import { LINKS } from "@/lib/constants";
|
||||
import { useEffect, useRef, useState, type ReactNode } from "react";
|
||||
import { Button, type ButtonVariantType } from "../ui/button";
|
||||
import { Link } from "@tanstack/react-router";
|
||||
import { Link, useMatchRoute, useParams } from "@tanstack/react-router";
|
||||
|
||||
import Logo from "@/icons/logo.svg?react";
|
||||
import LogoWithText from "@/icons/logo-with-text.svg?react";
|
||||
@@ -41,32 +42,6 @@ const MENU_ITEMS: MenuItemType[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const MenuItem = ({ item }: { item: MenuItemType }) => {
|
||||
const { isMainMenuOpen } = useAccountStore();
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={item.path}
|
||||
className="group flex items-center gap-3 w-full whitespace-nowrap"
|
||||
viewTransition={{ types: ["warp"] }}
|
||||
>
|
||||
<Button
|
||||
data-variant={item.variant}
|
||||
variant={item.variant}
|
||||
className="w-8 h-8 data-[variant=secondary]:group-hover:bg-fill-150 data-[variant=primary]:group-hover:bg-fill-700 transition-colors"
|
||||
>
|
||||
{item.icon}
|
||||
</Button>
|
||||
<div
|
||||
data-open={isMainMenuOpen}
|
||||
className="data-[open=true]:opacity-100 opacity-0 transition-opacity duration-200 font-[500] text-[14px]"
|
||||
>
|
||||
{item.name}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const MainMenu = observer(() => {
|
||||
const { isMainMenuOpen } = useAccountStore();
|
||||
|
||||
@@ -83,7 +58,6 @@ const MainMenu = observer(() => {
|
||||
const MOCK_CHATS = Array.from({ length: 20 }, (_, i) => ({
|
||||
id: i,
|
||||
title: `Chat ${i + 1}`,
|
||||
subtitle: `Last message preview...`,
|
||||
}));
|
||||
|
||||
const MainMenuContent = observer(() => {
|
||||
@@ -109,8 +83,9 @@ const MainMenuContent = observer(() => {
|
||||
|
||||
return (
|
||||
<div
|
||||
data-open={isMainMenuOpen}
|
||||
ref={containerRef}
|
||||
className="relative w-full h-full overflow-auto flex flex-col"
|
||||
className="relative w-full h-full overflow-x-hidden data-[open=true]:overflow-y-auto overflow-y-hidden flex flex-col"
|
||||
>
|
||||
<header
|
||||
data-scrolled={hasScrolled}
|
||||
@@ -160,19 +135,12 @@ const MainMenuContent = observer(() => {
|
||||
Recent
|
||||
</p>
|
||||
|
||||
<ul className="space-y-2 pb-10">
|
||||
<ul className=" pb-10">
|
||||
{MOCK_CHATS.map((chat) => (
|
||||
<li
|
||||
<ChatCell
|
||||
chat={{ id: chat.id.toString(), title: chat.title }}
|
||||
key={chat.id}
|
||||
className="rounded-md px-3 py-2 hover:bg-fill-100 cursor-pointer transition-colors"
|
||||
>
|
||||
<p className="text-sm font-medium text-text-light-900 truncate">
|
||||
{chat.title}
|
||||
</p>
|
||||
<p className="text-xs text-text-light-400 truncate">
|
||||
{chat.subtitle}
|
||||
</p>
|
||||
</li>
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -196,4 +164,63 @@ const MainMenuContent = observer(() => {
|
||||
);
|
||||
});
|
||||
|
||||
const MenuItem = ({ item }: { item: MenuItemType }) => {
|
||||
const { isMainMenuOpen } = useAccountStore();
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={item.path}
|
||||
className="group flex items-center gap-3 w-full whitespace-nowrap"
|
||||
viewTransition={{ types: ["warp"] }}
|
||||
>
|
||||
<Button
|
||||
data-variant={item.variant}
|
||||
variant={item.variant}
|
||||
className="w-8 h-8 data-[variant=secondary]:group-hover:bg-fill-150 data-[variant=primary]:group-hover:bg-fill-700 transition-colors"
|
||||
>
|
||||
{item.icon}
|
||||
</Button>
|
||||
<div
|
||||
data-open={isMainMenuOpen}
|
||||
className="data-[open=true]:opacity-100 opacity-0 transition-opacity duration-200 font-[500] text-[14px]"
|
||||
>
|
||||
{item.name}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const ChatCell = ({ chat }: { chat: { id: string; title: string } }) => {
|
||||
const matchRoute = useMatchRoute();
|
||||
const match = matchRoute({ to: "/interaction-panel/$id", fuzzy: true });
|
||||
|
||||
const activeChatId = match ? match.id : undefined;
|
||||
|
||||
return (
|
||||
<div
|
||||
data-active={activeChatId === chat.id}
|
||||
className="data-[active=true]:bg-fill-150 block rounded-md h-10 hover:bg-fill-100 cursor-pointer transition-colors group flex items-center"
|
||||
>
|
||||
<Link
|
||||
to="/interaction-panel/$id"
|
||||
params={{ id: chat.id }}
|
||||
viewTransition={{ types: ["warp"] }}
|
||||
key={chat.id}
|
||||
className="flex-1 h-full items-center flex pl-3"
|
||||
>
|
||||
<div className="text-sm font-medium text-text-light-900 truncate">
|
||||
{chat.title}
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<button
|
||||
className="opacity-0 group-hover:opacity-100 duration-300 cursor-pointer flex items-center pr-3 justify-end w-5 h-full"
|
||||
type="button"
|
||||
>
|
||||
<ThreeDots />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { MainMenu, MenuItem, MENU_ITEMS, MainMenuContent };
|
||||
|
||||
3
src/icons/three-dots.svg
Normal file
3
src/icons/three-dots.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="2" height="10" viewBox="0 0 2 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.75 5C1.75 5.14834 1.70601 5.29334 1.6236 5.41668C1.54119 5.54002 1.42406 5.63614 1.28701 5.69291C1.14997 5.74968 0.999168 5.76453 0.853682 5.73559C0.708197 5.70665 0.57456 5.63522 0.46967 5.53033C0.364781 5.42544 0.29335 5.2918 0.264411 5.14632C0.235472 5.00083 0.250325 4.85003 0.307091 4.71299C0.363856 4.57594 0.459986 4.45881 0.583323 4.3764C0.70666 4.29399 0.851664 4.25 1 4.25C1.19891 4.25 1.38968 4.32902 1.53033 4.46967C1.67098 4.61032 1.75 4.80109 1.75 5ZM1 1.5C1.14834 1.5 1.29334 1.45601 1.41668 1.3736C1.54001 1.29119 1.63614 1.17406 1.69291 1.03701C1.74968 0.899968 1.76453 0.749169 1.73559 0.603683C1.70665 0.458197 1.63522 0.32456 1.53033 0.21967C1.42544 0.114781 1.2918 0.0433503 1.14632 0.0144114C1.00083 -0.0145275 0.850032 0.000324965 0.712988 0.0570907C0.575943 0.113856 0.458809 0.209986 0.376398 0.333323C0.293987 0.45666 0.25 0.601664 0.25 0.750001C0.25 0.948913 0.329018 1.13968 0.46967 1.28033C0.610322 1.42098 0.801088 1.5 1 1.5ZM1 8.5C0.851664 8.5 0.70666 8.54399 0.583323 8.6264C0.459986 8.70881 0.363856 8.82594 0.307091 8.96299C0.250325 9.10003 0.235472 9.25083 0.264411 9.39632C0.29335 9.5418 0.364781 9.67544 0.46967 9.78033C0.57456 9.88522 0.708197 9.95665 0.853682 9.98559C0.999168 10.0145 1.14997 9.99968 1.28701 9.94291C1.42406 9.88614 1.54119 9.79002 1.6236 9.66668C1.70601 9.54334 1.75 9.39834 1.75 9.25C1.75 9.05109 1.67098 8.86032 1.53033 8.71967C1.38968 8.57902 1.19891 8.5 1 8.5Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -9,19 +9,14 @@
|
||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||
|
||||
import { Route as rootRouteImport } from './routes/__root'
|
||||
import { Route as InteractionPanelRouteImport } from './routes/interaction-panel'
|
||||
import { Route as AuthRouteImport } from './routes/auth'
|
||||
import { Route as IndexRouteImport } from './routes/index'
|
||||
import { Route as MagicLinkMagicLinkRouteImport } from './routes/magic-link/$magic-link'
|
||||
import { Route as InteractionPanelIdRouteImport } from './routes/interaction-panel/$id'
|
||||
import { Route as AuthUsernameRouteImport } from './routes/auth/username'
|
||||
import { Route as AuthMailRouteImport } from './routes/auth/mail'
|
||||
import { Route as AuthCodeRouteImport } from './routes/auth/code'
|
||||
|
||||
const InteractionPanelRoute = InteractionPanelRouteImport.update({
|
||||
id: '/interaction-panel',
|
||||
path: '/interaction-panel',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const AuthRoute = AuthRouteImport.update({
|
||||
id: '/auth',
|
||||
path: '/auth',
|
||||
@@ -37,6 +32,11 @@ const MagicLinkMagicLinkRoute = MagicLinkMagicLinkRouteImport.update({
|
||||
path: '/magic-link/$magic-link',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const InteractionPanelIdRoute = InteractionPanelIdRouteImport.update({
|
||||
id: '/interaction-panel/$id',
|
||||
path: '/interaction-panel/$id',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const AuthUsernameRoute = AuthUsernameRouteImport.update({
|
||||
id: '/username',
|
||||
path: '/username',
|
||||
@@ -56,29 +56,29 @@ const AuthCodeRoute = AuthCodeRouteImport.update({
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'/auth': typeof AuthRouteWithChildren
|
||||
'/interaction-panel': typeof InteractionPanelRoute
|
||||
'/auth/code': typeof AuthCodeRoute
|
||||
'/auth/mail': typeof AuthMailRoute
|
||||
'/auth/username': typeof AuthUsernameRoute
|
||||
'/interaction-panel/$id': typeof InteractionPanelIdRoute
|
||||
'/magic-link/$magic-link': typeof MagicLinkMagicLinkRoute
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/auth': typeof AuthRouteWithChildren
|
||||
'/interaction-panel': typeof InteractionPanelRoute
|
||||
'/auth/code': typeof AuthCodeRoute
|
||||
'/auth/mail': typeof AuthMailRoute
|
||||
'/auth/username': typeof AuthUsernameRoute
|
||||
'/interaction-panel/$id': typeof InteractionPanelIdRoute
|
||||
'/magic-link/$magic-link': typeof MagicLinkMagicLinkRoute
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
'/': typeof IndexRoute
|
||||
'/auth': typeof AuthRouteWithChildren
|
||||
'/interaction-panel': typeof InteractionPanelRoute
|
||||
'/auth/code': typeof AuthCodeRoute
|
||||
'/auth/mail': typeof AuthMailRoute
|
||||
'/auth/username': typeof AuthUsernameRoute
|
||||
'/interaction-panel/$id': typeof InteractionPanelIdRoute
|
||||
'/magic-link/$magic-link': typeof MagicLinkMagicLinkRoute
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
@@ -86,47 +86,40 @@ export interface FileRouteTypes {
|
||||
fullPaths:
|
||||
| '/'
|
||||
| '/auth'
|
||||
| '/interaction-panel'
|
||||
| '/auth/code'
|
||||
| '/auth/mail'
|
||||
| '/auth/username'
|
||||
| '/interaction-panel/$id'
|
||||
| '/magic-link/$magic-link'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to:
|
||||
| '/'
|
||||
| '/auth'
|
||||
| '/interaction-panel'
|
||||
| '/auth/code'
|
||||
| '/auth/mail'
|
||||
| '/auth/username'
|
||||
| '/interaction-panel/$id'
|
||||
| '/magic-link/$magic-link'
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
| '/auth'
|
||||
| '/interaction-panel'
|
||||
| '/auth/code'
|
||||
| '/auth/mail'
|
||||
| '/auth/username'
|
||||
| '/interaction-panel/$id'
|
||||
| '/magic-link/$magic-link'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
AuthRoute: typeof AuthRouteWithChildren
|
||||
InteractionPanelRoute: typeof InteractionPanelRoute
|
||||
InteractionPanelIdRoute: typeof InteractionPanelIdRoute
|
||||
MagicLinkMagicLinkRoute: typeof MagicLinkMagicLinkRoute
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface FileRoutesByPath {
|
||||
'/interaction-panel': {
|
||||
id: '/interaction-panel'
|
||||
path: '/interaction-panel'
|
||||
fullPath: '/interaction-panel'
|
||||
preLoaderRoute: typeof InteractionPanelRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/auth': {
|
||||
id: '/auth'
|
||||
path: '/auth'
|
||||
@@ -148,6 +141,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof MagicLinkMagicLinkRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/interaction-panel/$id': {
|
||||
id: '/interaction-panel/$id'
|
||||
path: '/interaction-panel/$id'
|
||||
fullPath: '/interaction-panel/$id'
|
||||
preLoaderRoute: typeof InteractionPanelIdRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/auth/username': {
|
||||
id: '/auth/username'
|
||||
path: '/username'
|
||||
@@ -189,7 +189,7 @@ const AuthRouteWithChildren = AuthRoute._addFileChildren(AuthRouteChildren)
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
AuthRoute: AuthRouteWithChildren,
|
||||
InteractionPanelRoute: InteractionPanelRoute,
|
||||
InteractionPanelIdRoute: InteractionPanelIdRoute,
|
||||
MagicLinkMagicLinkRoute: MagicLinkMagicLinkRoute,
|
||||
}
|
||||
export const routeTree = rootRouteImport
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
// src/routes/__root.tsx
|
||||
import { Outlet, createRootRoute } from "@tanstack/react-router";
|
||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
||||
import { useAccountStore } from "@/contexts/AccountContext";
|
||||
import { MainMenu } from "@/components/main-menu/main-menu";
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
export const Route = createRootRoute({
|
||||
component: RootLayout,
|
||||
component: () => <RootLayout />,
|
||||
});
|
||||
|
||||
function RootLayout() {
|
||||
const RootLayout = observer(() => {
|
||||
const { userId } = useAccountStore();
|
||||
|
||||
return (
|
||||
@@ -24,4 +23,4 @@ function RootLayout() {
|
||||
{/* <TanStackRouterDevtools /> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useAccountStore } from "@/contexts/AccountContext";
|
||||
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useState } from "react";
|
||||
|
||||
export const Route = createFileRoute("/auth/username")({
|
||||
component: RouteComponent,
|
||||
component: () => <RouteComponent />,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const RouteComponent = observer(() => {
|
||||
const [username, setUsername] = useState("");
|
||||
const navigate = useNavigate();
|
||||
const { setUserId } = useAccountStore();
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (username.trim() === "") return;
|
||||
|
||||
setUserId("ID");
|
||||
navigate({
|
||||
to: "/",
|
||||
viewTransition: { types: ["warp"] },
|
||||
@@ -67,4 +72,4 @@ function RouteComponent() {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
import { Chat } from "@/components/interaction-panel/chat";
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
|
||||
import LogoWithText from "@/icons/logo-with-text.svg?react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useAccountStore } from "@/contexts/AccountContext";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import Clock from "@/icons/clock.svg?react";
|
||||
import { ChatInput } from "@/components/interaction-panel/chat";
|
||||
|
||||
export const Route = createFileRoute("/")({
|
||||
component: App,
|
||||
component: () => <App />,
|
||||
});
|
||||
|
||||
function App() {
|
||||
const App = observer(() => {
|
||||
const { userId } = useAccountStore();
|
||||
|
||||
return (
|
||||
<div className="h-full pt-5">
|
||||
<div className="h-full ">
|
||||
{!userId && (
|
||||
<header className="px-10 h-[52px] flex items-center justify-between absolute w-full">
|
||||
<header className=" pt-5 px-10 h-[52px] flex items-center justify-between absolute w-full">
|
||||
<LogoWithText />
|
||||
<Link to="/auth/mail" viewTransition={{ types: ["warp"] }}>
|
||||
<Button className=" text-[18px] w-[106px] cursor-pointer">
|
||||
@@ -23,7 +25,64 @@ function App() {
|
||||
</Link>
|
||||
</header>
|
||||
)}
|
||||
<Chat />
|
||||
|
||||
<div className="flex-1 h-full flex items-center justify-center">
|
||||
<WelcomePanel />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const WelcomePanel = () => {
|
||||
const navigate = useNavigate();
|
||||
const { userId } = useAccountStore();
|
||||
|
||||
return (
|
||||
<div className="max-w-[741px] flex flex-col gap-8">
|
||||
<div className="flex flex-col gap-2 font-medium">
|
||||
<span className="leading-[120%] text-black text-[24px]">
|
||||
Hi!
|
||||
<br /> I will help you create a crypto project on Cytonic
|
||||
</span>
|
||||
<span className="text-[#C0C0C0] text-[14px] flex gap-2 items-center">
|
||||
<Clock />
|
||||
<p>Estimated build time: 3 minutes</p>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<p className="text-[#C0C0C0] text-[14px] font-medium leading-[120%]">
|
||||
Choose one of the most common prompts
|
||||
<br /> below or use your own to begin
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
{[
|
||||
{ emoji: "🐶", text: "Build a memecoin launchpad" },
|
||||
{ emoji: "🏦", text: "Build a new stablecoin protocol" },
|
||||
{ emoji: "🌐", text: "Create a RWA marketplace on Cytonic" },
|
||||
{ emoji: "📊", text: "Ship a token dashboard" },
|
||||
].map(({ emoji, text }) => (
|
||||
<button
|
||||
type="button"
|
||||
key={emoji}
|
||||
onClick={() => {
|
||||
if (userId === undefined) {
|
||||
navigate({
|
||||
to: "/auth/mail",
|
||||
viewTransition: { types: ["warp"] },
|
||||
});
|
||||
}
|
||||
}}
|
||||
className="text-left cursor-pointer hover:bg-fill-100 transition-all w-[173px] h-[123px] p-4 rounded-[16px] border border-[#E8E8E8] flex flex-col justify-between"
|
||||
>
|
||||
<p className="text-[14px] font-medium leading-[120%]">{text}</p>
|
||||
<div className="text-[24px] leading-[100%]">{emoji}</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<ChatInput />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
// src/routes/chat.tsx
|
||||
import { Chat } from "@/components/interaction-panel/chat";
|
||||
import { Sandbox } from "@/components/interaction-panel/sandbox";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/interaction-panel")({
|
||||
component: InteractionPanelRoute,
|
||||
export const Route = createFileRoute("/interaction-panel/$id")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function InteractionPanelRoute() {
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="flex h-full">
|
||||
<Chat />
|
||||
Reference in New Issue
Block a user