update toaster LO; add forms where in necessary
This commit is contained in:
@@ -3,7 +3,12 @@ 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 { useAccountStore } from "@/contexts/AccountContext";
|
||||
import { Button } from "../ui/button";
|
||||
import { useToast } from "@/lib/hooks/use-toast";
|
||||
|
||||
const Chat = observer(() => {
|
||||
return (
|
||||
@@ -14,6 +19,10 @@ const Chat = observer(() => {
|
||||
});
|
||||
|
||||
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">
|
||||
@@ -42,6 +51,14 @@ const WelcomePanel = () => {
|
||||
<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>
|
||||
@@ -57,6 +74,9 @@ const WelcomePanel = () => {
|
||||
};
|
||||
|
||||
const ChatInput = () => {
|
||||
const navigate = useNavigate();
|
||||
const { userId } = useAccountStore();
|
||||
|
||||
const [message, setMessage] = useState("");
|
||||
const [showShadow, setShowShadow] = useState(false);
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
@@ -116,14 +136,20 @@ const ChatInput = () => {
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
type="button"
|
||||
<Button
|
||||
className="w-10 h-10 rotate-180"
|
||||
disabled={message.trim() === ""}
|
||||
className="w-10 h-10 rounded-[8px] flex items-center justify-center rotate-180 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
style={{ backgroundColor: "#E8E8E8" }}
|
||||
onClick={() => {
|
||||
if (userId === undefined) {
|
||||
navigate({
|
||||
to: "/auth/mail",
|
||||
viewTransition: { types: ["warp"] },
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Arrow color="white" />
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ const buttonVariants = cva(
|
||||
variants: {
|
||||
variant: {
|
||||
primary:
|
||||
"bg-fill-800 text-white hover:bg-fill-700 disabled:bg-fill-400",
|
||||
"bg-fill-800 text-white hover:bg-fill-700 disabled:bg-fill-200",
|
||||
secondary:
|
||||
"bg-fill-100 text-text-light-900 hover:bg-fill-150 disabled:bg-fill-100 disabled:text-text-light-200",
|
||||
tertiary:
|
||||
|
||||
@@ -17,7 +17,7 @@ export const TextLink: FC<TextLinkProps> = ({
|
||||
<a
|
||||
data-disabled={disabled}
|
||||
className={cn(
|
||||
"text-feedback-info-900 data-[disabled=true]:opacity-50 font-[600] data-[disabled=false]:cursor-pointer data-[disabled=false]:hover:border-b-[2px] w-fit transition-all duration-75",
|
||||
"text-feedback-info-900 data-[disabled=true]:opacity-50 font-[500] data-[disabled=false]:cursor-pointer data-[disabled=false]:hover:border-b-[2px] w-fit transition-all duration-75",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -13,6 +13,10 @@ import { cn } from "@/lib/utils";
|
||||
import clsx from "clsx";
|
||||
import { useToast } from "@/lib/hooks/use-toast";
|
||||
|
||||
import Discord from "@/icons/discord.svg?react";
|
||||
import Copy from "@/icons/copy.svg?react";
|
||||
import { TextLink } from "./text-link";
|
||||
|
||||
const ToastProvider = ToastPrimitives.Provider;
|
||||
|
||||
const ToastViewport = React.forwardRef<
|
||||
@@ -35,10 +39,10 @@ const toastVariants = cva(
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
success: "bg-feedback-positive-100",
|
||||
warning: "bg-feedback-attention-100",
|
||||
error: "bg-feedback-negative-100",
|
||||
info: "bg-feedback-info-100",
|
||||
success: "bg-fill-100",
|
||||
warning: "bg-fill-100",
|
||||
error: "bg-fill-100",
|
||||
info: "bg-fill-100",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@@ -51,7 +55,7 @@ const ToastIcon = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
|
||||
VariantProps<typeof toastVariants>
|
||||
>(({ className, variant }) => {
|
||||
>(({ className, variant = "success" }) => {
|
||||
if (variant === "success") {
|
||||
return (
|
||||
<SuccessIcon className={clsx(className, "text-feedback-positive-900")} />
|
||||
@@ -213,7 +217,7 @@ type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
|
||||
|
||||
type ToastActionElement = React.ReactElement<typeof ToastAction>;
|
||||
|
||||
export function Toaster() {
|
||||
function Toaster() {
|
||||
const { toasts, isPaused, setIsPaused } = useToast();
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -258,28 +262,12 @@ export function Toaster() {
|
||||
>
|
||||
<ToastIcon className="size-12 mr-4" variant={props.variant} />
|
||||
<div className="grid gap-2 w-full">
|
||||
{title && <ToastTitle>{title}</ToastTitle>}
|
||||
<div className="flex items-center w-full justify-between">
|
||||
{title && <ToastTitle>{title}</ToastTitle>} <ToastClose />
|
||||
</div>
|
||||
|
||||
{description && (
|
||||
<ToastDescription>
|
||||
{/* {props.variant === "errorWithCopy" && (
|
||||
<p className="text-[16px] mb-2">
|
||||
To solve problems, contact us <br />
|
||||
via{" "}
|
||||
<a
|
||||
href={"https://discord.gg/cytonic"}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-brand relative"
|
||||
>
|
||||
Discord
|
||||
<div className="h-[1px] w-[56px] bg-brand absolute left-0" />
|
||||
</a>{" "}
|
||||
and send the error text
|
||||
</p>
|
||||
)} */}
|
||||
{description}
|
||||
</ToastDescription>
|
||||
<ToastDescription>{description}</ToastDescription>
|
||||
)}
|
||||
|
||||
{copy && (
|
||||
@@ -287,7 +275,6 @@ export function Toaster() {
|
||||
)}
|
||||
</div>
|
||||
{action}
|
||||
<ToastClose />
|
||||
|
||||
{withTimeline && (
|
||||
<ToastTimeline
|
||||
@@ -304,6 +291,35 @@ export function Toaster() {
|
||||
);
|
||||
}
|
||||
|
||||
const TOASTER_DESCRIPTION_PATTERNS = {
|
||||
errorPattern: (
|
||||
<div>
|
||||
<span>
|
||||
<p>Please try again later</p>
|
||||
<p className="mt-2">
|
||||
If the problem persists, copy the error message below and contact our
|
||||
support team
|
||||
</p>
|
||||
</span>
|
||||
|
||||
<div className="space-x-4 flex">
|
||||
<TextLink className="flex items-center gap-1">
|
||||
<Copy className="text-feedback-info-900" width={15} height={12} />[
|
||||
Error code ]
|
||||
</TextLink>
|
||||
|
||||
<TextLink
|
||||
className="flex items-center gap-1"
|
||||
href="https://discord.gg/cytonic"
|
||||
>
|
||||
<Discord className="text-feedback-info-900" width={15} height={12} />[
|
||||
Support ]
|
||||
</TextLink>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export {
|
||||
type ToastProps,
|
||||
type ToastActionElement,
|
||||
@@ -317,4 +333,6 @@ export {
|
||||
ToastIcon,
|
||||
ToastCopy,
|
||||
ToastTimeline,
|
||||
Toaster,
|
||||
TOASTER_DESCRIPTION_PATTERNS,
|
||||
};
|
||||
|
||||
3
src/icons/discord.svg
Normal file
3
src/icons/discord.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="18" viewBox="0 0 24 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.9831 1.50753C18.4748 0.794022 16.8791 0.287044 15.2381 0C15.0337 0.37359 14.7949 0.87607 14.6303 1.27582C12.8611 1.00684 11.1081 1.00684 9.37143 1.27582C9.20684 0.876161 8.96264 0.37359 8.75636 0C7.11383 0.287168 5.51674 0.795438 4.00767 1.51126C1.00474 6.09907 0.190662 10.5728 0.597654 14.9832C2.5894 16.4869 4.5196 17.4004 6.41728 17.9982C6.88893 17.3425 7.30584 16.6483 7.66368 15.9228C6.98233 15.6605 6.32548 15.3373 5.70096 14.9571C5.86529 14.8339 6.02577 14.7055 6.18216 14.5722C9.96656 16.3618 14.0785 16.3618 17.8178 14.5722C17.9749 14.7047 18.1353 14.833 18.2989 14.9571C17.6734 15.3384 17.0153 15.6622 16.3326 15.9247C16.6925 16.6531 17.1087 17.348 17.579 18C19.4785 17.4023 21.4105 16.4888 23.4022 14.9832C23.8798 9.87048 22.5865 5.43781 19.9831 1.50753ZM8.1793 12.2709C7.04322 12.2709 6.11154 11.1986 6.11154 9.89292C6.11154 8.58722 7.02337 7.51313 8.1793 7.51313C9.33523 7.51313 10.2669 8.58531 10.247 9.89292C10.2488 11.1986 9.33531 12.2709 8.1793 12.2709ZM15.8206 12.2709C14.6845 12.2709 13.7529 11.1986 13.7529 9.89292C13.7529 8.58722 14.6647 7.51313 15.8206 7.51313C16.9766 7.51313 17.9082 8.58531 17.8883 9.89292C17.8883 11.1986 16.9766 12.2709 15.8206 12.2709Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -12,7 +12,7 @@ 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 MailCodeMailCodeRouteImport } from './routes/mail-code/$mail-code'
|
||||
import { Route as MagicLinkMagicLinkRouteImport } from './routes/magic-link/$magic-link'
|
||||
import { Route as AuthUsernameRouteImport } from './routes/auth/username'
|
||||
import { Route as AuthMailRouteImport } from './routes/auth/mail'
|
||||
import { Route as AuthCodeRouteImport } from './routes/auth/code'
|
||||
@@ -32,9 +32,9 @@ const IndexRoute = IndexRouteImport.update({
|
||||
path: '/',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const MailCodeMailCodeRoute = MailCodeMailCodeRouteImport.update({
|
||||
id: '/mail-code/$mail-code',
|
||||
path: '/mail-code/$mail-code',
|
||||
const MagicLinkMagicLinkRoute = MagicLinkMagicLinkRouteImport.update({
|
||||
id: '/magic-link/$magic-link',
|
||||
path: '/magic-link/$magic-link',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const AuthUsernameRoute = AuthUsernameRouteImport.update({
|
||||
@@ -60,7 +60,7 @@ export interface FileRoutesByFullPath {
|
||||
'/auth/code': typeof AuthCodeRoute
|
||||
'/auth/mail': typeof AuthMailRoute
|
||||
'/auth/username': typeof AuthUsernameRoute
|
||||
'/mail-code/$mail-code': typeof MailCodeMailCodeRoute
|
||||
'/magic-link/$magic-link': typeof MagicLinkMagicLinkRoute
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
@@ -69,7 +69,7 @@ export interface FileRoutesByTo {
|
||||
'/auth/code': typeof AuthCodeRoute
|
||||
'/auth/mail': typeof AuthMailRoute
|
||||
'/auth/username': typeof AuthUsernameRoute
|
||||
'/mail-code/$mail-code': typeof MailCodeMailCodeRoute
|
||||
'/magic-link/$magic-link': typeof MagicLinkMagicLinkRoute
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
@@ -79,7 +79,7 @@ export interface FileRoutesById {
|
||||
'/auth/code': typeof AuthCodeRoute
|
||||
'/auth/mail': typeof AuthMailRoute
|
||||
'/auth/username': typeof AuthUsernameRoute
|
||||
'/mail-code/$mail-code': typeof MailCodeMailCodeRoute
|
||||
'/magic-link/$magic-link': typeof MagicLinkMagicLinkRoute
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
@@ -90,7 +90,7 @@ export interface FileRouteTypes {
|
||||
| '/auth/code'
|
||||
| '/auth/mail'
|
||||
| '/auth/username'
|
||||
| '/mail-code/$mail-code'
|
||||
| '/magic-link/$magic-link'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to:
|
||||
| '/'
|
||||
@@ -99,7 +99,7 @@ export interface FileRouteTypes {
|
||||
| '/auth/code'
|
||||
| '/auth/mail'
|
||||
| '/auth/username'
|
||||
| '/mail-code/$mail-code'
|
||||
| '/magic-link/$magic-link'
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
@@ -108,14 +108,14 @@ export interface FileRouteTypes {
|
||||
| '/auth/code'
|
||||
| '/auth/mail'
|
||||
| '/auth/username'
|
||||
| '/mail-code/$mail-code'
|
||||
| '/magic-link/$magic-link'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
AuthRoute: typeof AuthRouteWithChildren
|
||||
InteractionPanelRoute: typeof InteractionPanelRoute
|
||||
MailCodeMailCodeRoute: typeof MailCodeMailCodeRoute
|
||||
MagicLinkMagicLinkRoute: typeof MagicLinkMagicLinkRoute
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
@@ -141,11 +141,11 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof IndexRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/mail-code/$mail-code': {
|
||||
id: '/mail-code/$mail-code'
|
||||
path: '/mail-code/$mail-code'
|
||||
fullPath: '/mail-code/$mail-code'
|
||||
preLoaderRoute: typeof MailCodeMailCodeRouteImport
|
||||
'/magic-link/$magic-link': {
|
||||
id: '/magic-link/$magic-link'
|
||||
path: '/magic-link/$magic-link'
|
||||
fullPath: '/magic-link/$magic-link'
|
||||
preLoaderRoute: typeof MagicLinkMagicLinkRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/auth/username': {
|
||||
@@ -190,7 +190,7 @@ const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
AuthRoute: AuthRouteWithChildren,
|
||||
InteractionPanelRoute: InteractionPanelRoute,
|
||||
MailCodeMailCodeRoute: MailCodeMailCodeRoute,
|
||||
MagicLinkMagicLinkRoute: MagicLinkMagicLinkRoute,
|
||||
}
|
||||
export const routeTree = rootRouteImport
|
||||
._addFileChildren(rootRouteChildren)
|
||||
|
||||
@@ -101,6 +101,15 @@ function RouteComponent() {
|
||||
setResendTimeout(60);
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
const fullCode = code.join("");
|
||||
console.log("Confirm code:", fullCode);
|
||||
navigate({
|
||||
to: "/auth/username",
|
||||
viewTransition: { types: ["warp"] },
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full gap-10 tracking-[-2%] flex-col">
|
||||
<div className="text-brand-gray text-center">
|
||||
@@ -114,7 +123,15 @@ function RouteComponent() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-10">
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
if (code.every((c) => c !== "")) {
|
||||
handleSubmit();
|
||||
}
|
||||
}}
|
||||
className="space-y-10"
|
||||
>
|
||||
<div className="flex space-x-2">
|
||||
{code.map((digit, i) => (
|
||||
<Input
|
||||
@@ -136,16 +153,9 @@ function RouteComponent() {
|
||||
|
||||
<div className="space-y-4">
|
||||
<Button
|
||||
type="submit"
|
||||
className="text-[18px] w-full"
|
||||
disabled={code.some((c) => c === "")}
|
||||
onClick={() => {
|
||||
const fullCode = code.join("");
|
||||
console.log("Confirm code:", fullCode);
|
||||
navigate({
|
||||
to: "/auth/username",
|
||||
viewTransition: { types: ["warp"] },
|
||||
});
|
||||
}}
|
||||
>
|
||||
Confirm
|
||||
</Button>
|
||||
@@ -165,7 +175,7 @@ function RouteComponent() {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,17 +29,19 @@ function RouteComponent() {
|
||||
</div>
|
||||
|
||||
<div className="space-y-6 w-[300px]">
|
||||
<Input
|
||||
label="Email"
|
||||
placeholder="Email@example.com"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
handleSubmit();
|
||||
}
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleSubmit();
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<Input
|
||||
label="Email"
|
||||
placeholder="Email@example.com"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
</form>
|
||||
|
||||
<Button
|
||||
className="text-[18px] w-full"
|
||||
|
||||
@@ -32,17 +32,19 @@ function RouteComponent() {
|
||||
</div>
|
||||
|
||||
<div className="space-y-6 w-[300px]">
|
||||
<Input
|
||||
label="Username"
|
||||
placeholder="Pink Axolotl"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
handleSubmit();
|
||||
}
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleSubmit();
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<Input
|
||||
label="Username"
|
||||
placeholder="Pink Axolotl"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
/>
|
||||
</form>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Button
|
||||
@@ -55,7 +57,9 @@ function RouteComponent() {
|
||||
<Button
|
||||
variant={"secondary"}
|
||||
className="text-[18px] w-full"
|
||||
// onClick={handleSubmit}
|
||||
onClick={() => {
|
||||
navigate({ to: "/", viewTransition: { types: ["warp"] } });
|
||||
}}
|
||||
>
|
||||
Skip for now
|
||||
</Button>
|
||||
|
||||
@@ -5,13 +5,13 @@ import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
|
||||
import { TextLink } from "@/components/ui/text-link";
|
||||
import LogoWithText from "@/icons/logo-with-text.svg?react";
|
||||
|
||||
export const Route = createFileRoute("/mail-code/$mail-code")({
|
||||
export const Route = createFileRoute("/magic-link/$magic-link")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const navigate = useNavigate();
|
||||
const { "mail-code": codeFromUrl } = Route.useParams();
|
||||
const { "magic-link": magicLink } = Route.useParams();
|
||||
|
||||
const [code, setCode] = useState<string[]>(Array(6).fill(""));
|
||||
const [isAutoSubmitted, setIsAutoSubmitted] = useState(false);
|
||||
@@ -31,8 +31,8 @@ function RouteComponent() {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (codeFromUrl && /^\d{6}$/.test(codeFromUrl)) {
|
||||
const codeArray = codeFromUrl.split("").slice(0, 6);
|
||||
if (magicLink && /^\d{6}$/.test(magicLink)) {
|
||||
const codeArray = magicLink.split("").slice(0, 6);
|
||||
setCode(codeArray);
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
@@ -41,7 +41,7 @@ function RouteComponent() {
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [codeFromUrl]);
|
||||
}, [magicLink]);
|
||||
|
||||
useEffect(() => {
|
||||
if (resendTimeout <= 0) {
|
||||
@@ -143,7 +143,7 @@ function RouteComponent() {
|
||||
{code.map((digit, i) => (
|
||||
<Input
|
||||
key={i}
|
||||
id={`code-input-${i}`}
|
||||
id={"code-input-${i}"}
|
||||
className="w-10 h-10 p-0 text-center text-lg"
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
Reference in New Issue
Block a user