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 { useRef, useState } from "react";
|
||||||
import Arrow from "@/icons/arrow.svg?react";
|
import Arrow from "@/icons/arrow.svg?react";
|
||||||
import Paperclip from "@/icons/paperclip.svg?react";
|
import Paperclip from "@/icons/paperclip.svg?react";
|
||||||
import Clock from "@/icons/clock.svg?react";
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useNavigate } from "@tanstack/react-router";
|
import { useNavigate, useParams } from "@tanstack/react-router";
|
||||||
import { useAccountStore } from "@/contexts/AccountContext";
|
import { useAccountStore } from "@/contexts/AccountContext";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import { useToast } from "@/lib/hooks/use-toast";
|
|
||||||
|
|
||||||
const Chat = observer(() => {
|
const Chat = observer(() => {
|
||||||
|
const { id } = useParams({ from: "/interaction-panel/$id" });
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 h-full flex items-center justify-center">
|
<div className="flex-1 h-full flex items-center justify-center">{id}</div>
|
||||||
<WelcomePanel />
|
|
||||||
</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 ChatInput = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { userId } = useAccountStore();
|
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 Books from "@/icons/books.svg?react";
|
||||||
import Discord from "@/icons/discord.svg?react";
|
import Discord from "@/icons/discord.svg?react";
|
||||||
import Profile from "@/icons/profile.svg?react";
|
import Profile from "@/icons/profile.svg?react";
|
||||||
|
import ThreeDots from "@/icons/three-dots.svg?react";
|
||||||
|
|
||||||
import { LINKS } from "@/lib/constants";
|
import { LINKS } from "@/lib/constants";
|
||||||
import { useEffect, useRef, useState, type ReactNode } from "react";
|
import { useEffect, useRef, useState, type ReactNode } from "react";
|
||||||
import { Button, type ButtonVariantType } from "../ui/button";
|
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 Logo from "@/icons/logo.svg?react";
|
||||||
import LogoWithText from "@/icons/logo-with-text.svg?react";
|
import LogoWithText from "@/icons/logo-with-text.svg?react";
|
||||||
@@ -41,39 +42,13 @@ 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 MainMenu = observer(() => {
|
||||||
const { isMainMenuOpen } = useAccountStore();
|
const { isMainMenuOpen } = useAccountStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-open={isMainMenuOpen}
|
data-open={isMainMenuOpen}
|
||||||
className="w-[64px] data-[open=true]:w-[227px] transition-all border-r border-r border-fill-150"
|
className="w-[64px] data-[open=true]:w-[227px] transition-all border-r border-r border-fill-150"
|
||||||
>
|
>
|
||||||
<MainMenuContent />
|
<MainMenuContent />
|
||||||
</div>
|
</div>
|
||||||
@@ -83,7 +58,6 @@ const MainMenu = observer(() => {
|
|||||||
const MOCK_CHATS = Array.from({ length: 20 }, (_, i) => ({
|
const MOCK_CHATS = Array.from({ length: 20 }, (_, i) => ({
|
||||||
id: i,
|
id: i,
|
||||||
title: `Chat ${i + 1}`,
|
title: `Chat ${i + 1}`,
|
||||||
subtitle: `Last message preview...`,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const MainMenuContent = observer(() => {
|
const MainMenuContent = observer(() => {
|
||||||
@@ -109,8 +83,9 @@ const MainMenuContent = observer(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
data-open={isMainMenuOpen}
|
||||||
ref={containerRef}
|
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
|
<header
|
||||||
data-scrolled={hasScrolled}
|
data-scrolled={hasScrolled}
|
||||||
@@ -160,19 +135,12 @@ const MainMenuContent = observer(() => {
|
|||||||
Recent
|
Recent
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<ul className="space-y-2 pb-10">
|
<ul className=" pb-10">
|
||||||
{MOCK_CHATS.map((chat) => (
|
{MOCK_CHATS.map((chat) => (
|
||||||
<li
|
<ChatCell
|
||||||
|
chat={{ id: chat.id.toString(), title: chat.title }}
|
||||||
key={chat.id}
|
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>
|
</ul>
|
||||||
</div>
|
</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 };
|
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.
|
// 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 rootRouteImport } from './routes/__root'
|
||||||
import { Route as InteractionPanelRouteImport } from './routes/interaction-panel'
|
|
||||||
import { Route as AuthRouteImport } from './routes/auth'
|
import { Route as AuthRouteImport } from './routes/auth'
|
||||||
import { Route as IndexRouteImport } from './routes/index'
|
import { Route as IndexRouteImport } from './routes/index'
|
||||||
import { Route as MagicLinkMagicLinkRouteImport } from './routes/magic-link/$magic-link'
|
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 AuthUsernameRouteImport } from './routes/auth/username'
|
||||||
import { Route as AuthMailRouteImport } from './routes/auth/mail'
|
import { Route as AuthMailRouteImport } from './routes/auth/mail'
|
||||||
import { Route as AuthCodeRouteImport } from './routes/auth/code'
|
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({
|
const AuthRoute = AuthRouteImport.update({
|
||||||
id: '/auth',
|
id: '/auth',
|
||||||
path: '/auth',
|
path: '/auth',
|
||||||
@@ -37,6 +32,11 @@ const MagicLinkMagicLinkRoute = MagicLinkMagicLinkRouteImport.update({
|
|||||||
path: '/magic-link/$magic-link',
|
path: '/magic-link/$magic-link',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
|
const InteractionPanelIdRoute = InteractionPanelIdRouteImport.update({
|
||||||
|
id: '/interaction-panel/$id',
|
||||||
|
path: '/interaction-panel/$id',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
const AuthUsernameRoute = AuthUsernameRouteImport.update({
|
const AuthUsernameRoute = AuthUsernameRouteImport.update({
|
||||||
id: '/username',
|
id: '/username',
|
||||||
path: '/username',
|
path: '/username',
|
||||||
@@ -56,29 +56,29 @@ const AuthCodeRoute = AuthCodeRouteImport.update({
|
|||||||
export interface FileRoutesByFullPath {
|
export interface FileRoutesByFullPath {
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/auth': typeof AuthRouteWithChildren
|
'/auth': typeof AuthRouteWithChildren
|
||||||
'/interaction-panel': typeof InteractionPanelRoute
|
|
||||||
'/auth/code': typeof AuthCodeRoute
|
'/auth/code': typeof AuthCodeRoute
|
||||||
'/auth/mail': typeof AuthMailRoute
|
'/auth/mail': typeof AuthMailRoute
|
||||||
'/auth/username': typeof AuthUsernameRoute
|
'/auth/username': typeof AuthUsernameRoute
|
||||||
|
'/interaction-panel/$id': typeof InteractionPanelIdRoute
|
||||||
'/magic-link/$magic-link': typeof MagicLinkMagicLinkRoute
|
'/magic-link/$magic-link': typeof MagicLinkMagicLinkRoute
|
||||||
}
|
}
|
||||||
export interface FileRoutesByTo {
|
export interface FileRoutesByTo {
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/auth': typeof AuthRouteWithChildren
|
'/auth': typeof AuthRouteWithChildren
|
||||||
'/interaction-panel': typeof InteractionPanelRoute
|
|
||||||
'/auth/code': typeof AuthCodeRoute
|
'/auth/code': typeof AuthCodeRoute
|
||||||
'/auth/mail': typeof AuthMailRoute
|
'/auth/mail': typeof AuthMailRoute
|
||||||
'/auth/username': typeof AuthUsernameRoute
|
'/auth/username': typeof AuthUsernameRoute
|
||||||
|
'/interaction-panel/$id': typeof InteractionPanelIdRoute
|
||||||
'/magic-link/$magic-link': typeof MagicLinkMagicLinkRoute
|
'/magic-link/$magic-link': typeof MagicLinkMagicLinkRoute
|
||||||
}
|
}
|
||||||
export interface FileRoutesById {
|
export interface FileRoutesById {
|
||||||
__root__: typeof rootRouteImport
|
__root__: typeof rootRouteImport
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/auth': typeof AuthRouteWithChildren
|
'/auth': typeof AuthRouteWithChildren
|
||||||
'/interaction-panel': typeof InteractionPanelRoute
|
|
||||||
'/auth/code': typeof AuthCodeRoute
|
'/auth/code': typeof AuthCodeRoute
|
||||||
'/auth/mail': typeof AuthMailRoute
|
'/auth/mail': typeof AuthMailRoute
|
||||||
'/auth/username': typeof AuthUsernameRoute
|
'/auth/username': typeof AuthUsernameRoute
|
||||||
|
'/interaction-panel/$id': typeof InteractionPanelIdRoute
|
||||||
'/magic-link/$magic-link': typeof MagicLinkMagicLinkRoute
|
'/magic-link/$magic-link': typeof MagicLinkMagicLinkRoute
|
||||||
}
|
}
|
||||||
export interface FileRouteTypes {
|
export interface FileRouteTypes {
|
||||||
@@ -86,47 +86,40 @@ export interface FileRouteTypes {
|
|||||||
fullPaths:
|
fullPaths:
|
||||||
| '/'
|
| '/'
|
||||||
| '/auth'
|
| '/auth'
|
||||||
| '/interaction-panel'
|
|
||||||
| '/auth/code'
|
| '/auth/code'
|
||||||
| '/auth/mail'
|
| '/auth/mail'
|
||||||
| '/auth/username'
|
| '/auth/username'
|
||||||
|
| '/interaction-panel/$id'
|
||||||
| '/magic-link/$magic-link'
|
| '/magic-link/$magic-link'
|
||||||
fileRoutesByTo: FileRoutesByTo
|
fileRoutesByTo: FileRoutesByTo
|
||||||
to:
|
to:
|
||||||
| '/'
|
| '/'
|
||||||
| '/auth'
|
| '/auth'
|
||||||
| '/interaction-panel'
|
|
||||||
| '/auth/code'
|
| '/auth/code'
|
||||||
| '/auth/mail'
|
| '/auth/mail'
|
||||||
| '/auth/username'
|
| '/auth/username'
|
||||||
|
| '/interaction-panel/$id'
|
||||||
| '/magic-link/$magic-link'
|
| '/magic-link/$magic-link'
|
||||||
id:
|
id:
|
||||||
| '__root__'
|
| '__root__'
|
||||||
| '/'
|
| '/'
|
||||||
| '/auth'
|
| '/auth'
|
||||||
| '/interaction-panel'
|
|
||||||
| '/auth/code'
|
| '/auth/code'
|
||||||
| '/auth/mail'
|
| '/auth/mail'
|
||||||
| '/auth/username'
|
| '/auth/username'
|
||||||
|
| '/interaction-panel/$id'
|
||||||
| '/magic-link/$magic-link'
|
| '/magic-link/$magic-link'
|
||||||
fileRoutesById: FileRoutesById
|
fileRoutesById: FileRoutesById
|
||||||
}
|
}
|
||||||
export interface RootRouteChildren {
|
export interface RootRouteChildren {
|
||||||
IndexRoute: typeof IndexRoute
|
IndexRoute: typeof IndexRoute
|
||||||
AuthRoute: typeof AuthRouteWithChildren
|
AuthRoute: typeof AuthRouteWithChildren
|
||||||
InteractionPanelRoute: typeof InteractionPanelRoute
|
InteractionPanelIdRoute: typeof InteractionPanelIdRoute
|
||||||
MagicLinkMagicLinkRoute: typeof MagicLinkMagicLinkRoute
|
MagicLinkMagicLinkRoute: typeof MagicLinkMagicLinkRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@tanstack/react-router' {
|
declare module '@tanstack/react-router' {
|
||||||
interface FileRoutesByPath {
|
interface FileRoutesByPath {
|
||||||
'/interaction-panel': {
|
|
||||||
id: '/interaction-panel'
|
|
||||||
path: '/interaction-panel'
|
|
||||||
fullPath: '/interaction-panel'
|
|
||||||
preLoaderRoute: typeof InteractionPanelRouteImport
|
|
||||||
parentRoute: typeof rootRouteImport
|
|
||||||
}
|
|
||||||
'/auth': {
|
'/auth': {
|
||||||
id: '/auth'
|
id: '/auth'
|
||||||
path: '/auth'
|
path: '/auth'
|
||||||
@@ -148,6 +141,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof MagicLinkMagicLinkRouteImport
|
preLoaderRoute: typeof MagicLinkMagicLinkRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
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': {
|
'/auth/username': {
|
||||||
id: '/auth/username'
|
id: '/auth/username'
|
||||||
path: '/username'
|
path: '/username'
|
||||||
@@ -189,7 +189,7 @@ const AuthRouteWithChildren = AuthRoute._addFileChildren(AuthRouteChildren)
|
|||||||
const rootRouteChildren: RootRouteChildren = {
|
const rootRouteChildren: RootRouteChildren = {
|
||||||
IndexRoute: IndexRoute,
|
IndexRoute: IndexRoute,
|
||||||
AuthRoute: AuthRouteWithChildren,
|
AuthRoute: AuthRouteWithChildren,
|
||||||
InteractionPanelRoute: InteractionPanelRoute,
|
InteractionPanelIdRoute: InteractionPanelIdRoute,
|
||||||
MagicLinkMagicLinkRoute: MagicLinkMagicLinkRoute,
|
MagicLinkMagicLinkRoute: MagicLinkMagicLinkRoute,
|
||||||
}
|
}
|
||||||
export const routeTree = rootRouteImport
|
export const routeTree = rootRouteImport
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
// src/routes/__root.tsx
|
// src/routes/__root.tsx
|
||||||
import { Outlet, createRootRoute } from "@tanstack/react-router";
|
import { Outlet, createRootRoute } from "@tanstack/react-router";
|
||||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
|
||||||
import { useAccountStore } from "@/contexts/AccountContext";
|
import { useAccountStore } from "@/contexts/AccountContext";
|
||||||
import { MainMenu } from "@/components/main-menu/main-menu";
|
import { MainMenu } from "@/components/main-menu/main-menu";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
|
||||||
export const Route = createRootRoute({
|
export const Route = createRootRoute({
|
||||||
component: RootLayout,
|
component: () => <RootLayout />,
|
||||||
});
|
});
|
||||||
|
const RootLayout = observer(() => {
|
||||||
function RootLayout() {
|
|
||||||
const { userId } = useAccountStore();
|
const { userId } = useAccountStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -24,4 +23,4 @@ function RootLayout() {
|
|||||||
{/* <TanStackRouterDevtools /> */}
|
{/* <TanStackRouterDevtools /> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { useAccountStore } from "@/contexts/AccountContext";
|
||||||
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
export const Route = createFileRoute("/auth/username")({
|
export const Route = createFileRoute("/auth/username")({
|
||||||
component: RouteComponent,
|
component: () => <RouteComponent />,
|
||||||
});
|
});
|
||||||
|
|
||||||
function RouteComponent() {
|
const RouteComponent = observer(() => {
|
||||||
const [username, setUsername] = useState("");
|
const [username, setUsername] = useState("");
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { setUserId } = useAccountStore();
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
if (username.trim() === "") return;
|
if (username.trim() === "") return;
|
||||||
|
|
||||||
|
setUserId("ID");
|
||||||
navigate({
|
navigate({
|
||||||
to: "/",
|
to: "/",
|
||||||
viewTransition: { types: ["warp"] },
|
viewTransition: { types: ["warp"] },
|
||||||
@@ -67,4 +72,4 @@ function RouteComponent() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
import { Chat } from "@/components/interaction-panel/chat";
|
import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
|
||||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
|
||||||
import LogoWithText from "@/icons/logo-with-text.svg?react";
|
import LogoWithText from "@/icons/logo-with-text.svg?react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useAccountStore } from "@/contexts/AccountContext";
|
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("/")({
|
export const Route = createFileRoute("/")({
|
||||||
component: App,
|
component: () => <App />,
|
||||||
});
|
});
|
||||||
|
|
||||||
function App() {
|
const App = observer(() => {
|
||||||
const { userId } = useAccountStore();
|
const { userId } = useAccountStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full pt-5">
|
<div className="h-full ">
|
||||||
{!userId && (
|
{!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 />
|
<LogoWithText />
|
||||||
<Link to="/auth/mail" viewTransition={{ types: ["warp"] }}>
|
<Link to="/auth/mail" viewTransition={{ types: ["warp"] }}>
|
||||||
<Button className=" text-[18px] w-[106px] cursor-pointer">
|
<Button className=" text-[18px] w-[106px] cursor-pointer">
|
||||||
@@ -23,7 +25,64 @@ function App() {
|
|||||||
</Link>
|
</Link>
|
||||||
</header>
|
</header>
|
||||||
)}
|
)}
|
||||||
<Chat />
|
|
||||||
|
<div className="flex-1 h-full flex items-center justify-center">
|
||||||
|
<WelcomePanel />
|
||||||
|
</div>
|
||||||
</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 { Chat } from "@/components/interaction-panel/chat";
|
||||||
import { Sandbox } from "@/components/interaction-panel/sandbox";
|
import { Sandbox } from "@/components/interaction-panel/sandbox";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
|
||||||
export const Route = createFileRoute("/interaction-panel")({
|
export const Route = createFileRoute("/interaction-panel/$id")({
|
||||||
component: InteractionPanelRoute,
|
component: RouteComponent,
|
||||||
});
|
});
|
||||||
|
|
||||||
function InteractionPanelRoute() {
|
function RouteComponent() {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full">
|
<div className="flex h-full">
|
||||||
<Chat />
|
<Chat />
|
||||||
Reference in New Issue
Block a user