mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-21 08:59:07 +01:00
Added react-router
This commit is contained in:
@@ -9,7 +9,8 @@ import { Sidebar } from './components/Sidebar';
|
||||
import { UrlBar } from './components/UrlBar';
|
||||
import { Grid } from './components/Grid';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useRequests, useWorkspace, useWorkspaces } from './hooks/useWorkspaces';
|
||||
import { useRequests } from './hooks/useWorkspaces';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
interface Response {
|
||||
url: string;
|
||||
@@ -21,17 +22,26 @@ interface Response {
|
||||
headers: Record<string, string>;
|
||||
}
|
||||
|
||||
type Params = {
|
||||
workspaceId: string;
|
||||
requestId?: string;
|
||||
};
|
||||
|
||||
function App() {
|
||||
const { data } = useWorkspace();
|
||||
console.log('DATA', data);
|
||||
const p = useParams<Params>();
|
||||
const workspaceId = p.workspaceId ?? '';
|
||||
const requestId = p.requestId;
|
||||
const { data: requests } = useRequests(workspaceId);
|
||||
const request = requests?.find((r) => r.id === requestId);
|
||||
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [response, setResponse] = useState<Response | null>(null);
|
||||
const [url, setUrl] = useState<string>('https://go-server.schier.dev/debug');
|
||||
const [body, setBody] = useState<string>(`{\n "foo": "bar"\n}`);
|
||||
const [url, setUrl] = useState<string>(request?.url ?? '');
|
||||
const [body, setBody] = useState<string>(request?.body ?? '');
|
||||
const [method, setMethod] = useState<{ label: string; value: string }>({
|
||||
label: 'GET',
|
||||
value: 'GET',
|
||||
label: request?.method ?? 'GET',
|
||||
value: request?.method ?? 'GET',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -44,9 +54,6 @@ function App() {
|
||||
return () => document.documentElement.removeEventListener('keypress', listener);
|
||||
}, []);
|
||||
|
||||
if (!data) return null;
|
||||
const { requests, workspace } = data;
|
||||
|
||||
async function sendRequest() {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
@@ -73,7 +80,7 @@ function App() {
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-[auto_1fr] h-full text-gray-900">
|
||||
<Sidebar requests={requests ?? []} workspaceId={workspace.id} />
|
||||
<Sidebar requests={requests ?? []} workspaceId={workspaceId} requestId={requestId} />
|
||||
<Grid cols={2}>
|
||||
<VStack className="w-full">
|
||||
<HStack as={WindowDragRegion} items="center" className="pl-3 pr-1.5">
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import { ButtonHTMLAttributes, forwardRef } from 'react';
|
||||
import { ButtonHTMLAttributes, ComponentPropsWithoutRef, ElementType } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||
export interface ButtonProps<T extends ElementType>
|
||||
extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
color?: 'primary' | 'secondary';
|
||||
size?: 'xs' | 'sm' | 'md';
|
||||
justify?: 'start' | 'center';
|
||||
forDropdown?: boolean;
|
||||
};
|
||||
as?: T;
|
||||
}
|
||||
|
||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
||||
{
|
||||
className,
|
||||
justify = 'center',
|
||||
children,
|
||||
size = 'md',
|
||||
forDropdown,
|
||||
color,
|
||||
...props
|
||||
}: ButtonProps,
|
||||
ref,
|
||||
) {
|
||||
export function Button<T extends ElementType>({
|
||||
className,
|
||||
as,
|
||||
justify = 'center',
|
||||
children,
|
||||
size = 'md',
|
||||
forDropdown,
|
||||
color,
|
||||
...props
|
||||
}: ButtonProps<T> & Omit<ComponentPropsWithoutRef<T>, keyof ButtonProps<T>>) {
|
||||
const Component = as || 'button';
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
<Component
|
||||
className={classnames(
|
||||
className,
|
||||
'rounded-md flex items-center',
|
||||
@@ -41,6 +41,6 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button
|
||||
>
|
||||
{children}
|
||||
{forDropdown && <Icon icon="triangle-down" className="ml-1 -mr-1" />}
|
||||
</button>
|
||||
</Component>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
||||
import { DropdownMenuRadioGroup } from '@radix-ui/react-dropdown-menu';
|
||||
import { motion } from 'framer-motion';
|
||||
import {
|
||||
CheckIcon,
|
||||
ChevronRightIcon,
|
||||
DotFilledIcon,
|
||||
HamburgerMenuIcon,
|
||||
} from '@radix-ui/react-icons';
|
||||
import { forwardRef, HTMLAttributes, ReactNode, useState } from 'react';
|
||||
import { Button } from './Button';
|
||||
import { CheckIcon } from '@radix-ui/react-icons';
|
||||
import { forwardRef, HTMLAttributes, ReactNode } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { HotKey } from './HotKey';
|
||||
|
||||
interface DropdownMenuRadioProps {
|
||||
children: ReactNode;
|
||||
@@ -232,13 +225,19 @@ function DropdownMenuSeparator({ className, ...props }: DropdownMenu.DropdownMen
|
||||
);
|
||||
}
|
||||
|
||||
function DropdownMenuTrigger({ className, ...props }: DropdownMenu.DropdownMenuTriggerProps) {
|
||||
function DropdownMenuTrigger({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: DropdownMenu.DropdownMenuTriggerProps) {
|
||||
return (
|
||||
<DropdownMenu.Trigger
|
||||
asChild
|
||||
className={classnames(className, 'focus:outline-none')}
|
||||
{...props}
|
||||
/>
|
||||
>
|
||||
<>{children}</>
|
||||
</DropdownMenu.Trigger>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import { forwardRef } from 'react';
|
||||
import { Icon, IconProps } from './Icon';
|
||||
import { Button, ButtonProps } from './Button';
|
||||
|
||||
type Props = Omit<IconProps, 'size'> & ButtonProps;
|
||||
type Props = Omit<IconProps, 'size'> & ButtonProps<typeof Button>;
|
||||
|
||||
export const IconButton = forwardRef<HTMLButtonElement, Props>(function IconButton(
|
||||
{ icon, spin, ...props }: Props,
|
||||
ref,
|
||||
) {
|
||||
export function IconButton({ icon, spin, ...props }: Props) {
|
||||
return (
|
||||
<Button ref={ref} className="group" {...props}>
|
||||
<Button className="group" {...props}>
|
||||
<Icon icon={icon} spin={spin} className="text-gray-700 group-hover:text-gray-900" />
|
||||
</Button>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,17 +3,19 @@ import classnames from 'classnames';
|
||||
import { IconButton } from './IconButton';
|
||||
import { Button } from './Button';
|
||||
import useTheme from '../hooks/useTheme';
|
||||
import { HStack } from './Stacks';
|
||||
import { HStack, VStack } from './Stacks';
|
||||
import { WindowDragRegion } from './WindowDragRegion';
|
||||
import { Request } from '../hooks/useWorkspaces';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
||||
workspaceId: string;
|
||||
requests: Request[];
|
||||
requestId?: string;
|
||||
}
|
||||
|
||||
export function Sidebar({ className, workspaceId, requests, ...props }: Props) {
|
||||
export function Sidebar({ className, requestId, workspaceId, requests, ...props }: Props) {
|
||||
const { toggleTheme } = useTheme();
|
||||
return (
|
||||
<div
|
||||
@@ -35,11 +37,13 @@ export function Sidebar({ className, workspaceId, requests, ...props }: Props) {
|
||||
}}
|
||||
/>
|
||||
</HStack>
|
||||
<ul className="mx-2 py-2">
|
||||
<VStack as="ul" className="py-2" space={1}>
|
||||
{requests.map((r) => (
|
||||
<li key={r.id}>
|
||||
<li key={r.id} className="mx-2">
|
||||
<Button
|
||||
className={classnames('w-full', false && 'bg-gray-50')}
|
||||
as={Link}
|
||||
to={`/workspaces/${workspaceId}/requests/${r.id}`}
|
||||
className={classnames('w-full', requestId === r.id && 'bg-gray-50')}
|
||||
size="sm"
|
||||
justify="start"
|
||||
>
|
||||
@@ -47,7 +51,7 @@ export function Sidebar({ className, workspaceId, requests, ...props }: Props) {
|
||||
</Button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</VStack>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,10 +27,9 @@ export function useWorkspaces(): UseQueryResult<Workspace[]> {
|
||||
});
|
||||
}
|
||||
|
||||
export function useRequests(): UseQueryResult<Request[]> {
|
||||
export function useRequests(workspaceId: string): UseQueryResult<Request[]> {
|
||||
return useQuery('requests', async () => {
|
||||
const workspaces = (await invoke('workspaces')) as Workspace[];
|
||||
const requests = (await invoke('requests', { workspaceId: workspaces[0].id })) as Request[];
|
||||
const requests = (await invoke('requests', { workspaceId })) as Request[];
|
||||
return requests.map(convertDates);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,29 +1,51 @@
|
||||
import React from 'react';
|
||||
import init, { greet } from 'hello';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
import { MotionConfig } from 'framer-motion';
|
||||
import init, { greet } from 'hello';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { setTheme } from './lib/theme';
|
||||
|
||||
import './main.css';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
||||
import { Layout } from './pages/Layout';
|
||||
import { Workspaces } from './pages/Workspaces';
|
||||
import './main.css';
|
||||
|
||||
setTheme();
|
||||
|
||||
await init();
|
||||
greet();
|
||||
await invoke('load_db');
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
element: <Layout />,
|
||||
children: [
|
||||
{
|
||||
path: '/',
|
||||
element: <Workspaces />,
|
||||
},
|
||||
{
|
||||
path: '/workspaces/:workspaceId',
|
||||
element: <App />,
|
||||
},
|
||||
{
|
||||
path: '/workspaces/:workspaceId/requests/:requestId',
|
||||
element: <App />,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<MotionConfig transition={{ duration: 0.15 }}>
|
||||
<HelmetProvider>
|
||||
<App />
|
||||
<RouterProvider router={router} />
|
||||
</HelmetProvider>
|
||||
</MotionConfig>
|
||||
</QueryClientProvider>
|
||||
|
||||
9
src-web/pages/Layout.tsx
Normal file
9
src-web/pages/Layout.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
export function Layout() {
|
||||
return (
|
||||
<div className="w-full h-full">
|
||||
<Outlet />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
15
src-web/pages/Workspaces.tsx
Normal file
15
src-web/pages/Workspaces.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
|
||||
export function Workspaces() {
|
||||
const workspaces = useWorkspaces();
|
||||
return (
|
||||
<ul className="p-12">
|
||||
{workspaces.data?.map((w) => (
|
||||
<Link key={w.id} to={`/workspaces/${w.id}`}>
|
||||
{w.name}
|
||||
</Link>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user