mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-19 23:31:21 +02:00
Added react-router
This commit is contained in:
63
package-lock.json
generated
63
package-lock.json
generated
@@ -31,7 +31,8 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-helmet-async": "^1.3.0",
|
"react-helmet-async": "^1.3.0",
|
||||||
"react-query": "^3.39.3"
|
"react-query": "^3.39.3",
|
||||||
|
"react-router-dom": "^6.8.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^1.2.2",
|
"@tauri-apps/cli": "^1.2.2",
|
||||||
@@ -1602,6 +1603,14 @@
|
|||||||
"@babel/runtime": "^7.13.10"
|
"@babel/runtime": "^7.13.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@remix-run/router": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@swc/core": {
|
"node_modules/@swc/core": {
|
||||||
"version": "1.3.35",
|
"version": "1.3.35",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.35.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.35.tgz",
|
||||||
@@ -5425,6 +5434,36 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
|
||||||
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
|
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/react-router": {
|
||||||
|
"version": "6.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.1.tgz",
|
||||||
|
"integrity": "sha512-Jgi8BzAJQ8MkPt8ipXnR73rnD7EmZ0HFFb7jdQU24TynGW1Ooqin2KVDN9voSC+7xhqbbCd2cjGUepb6RObnyg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@remix-run/router": "1.3.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-router-dom": {
|
||||||
|
"version": "6.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.1.tgz",
|
||||||
|
"integrity": "sha512-67EXNfkQgf34P7+PSb6VlBuaacGhkKn3kpE51+P6zYSG2kiRoumXEL6e27zTa9+PGF2MNXbgIUHTVlleLbIcHQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@remix-run/router": "1.3.2",
|
||||||
|
"react-router": "6.8.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8",
|
||||||
|
"react-dom": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-style-singleton": {
|
"node_modules/react-style-singleton": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
|
||||||
@@ -7571,6 +7610,11 @@
|
|||||||
"@babel/runtime": "^7.13.10"
|
"@babel/runtime": "^7.13.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@remix-run/router": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA=="
|
||||||
|
},
|
||||||
"@swc/core": {
|
"@swc/core": {
|
||||||
"version": "1.3.35",
|
"version": "1.3.35",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.35.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.35.tgz",
|
||||||
@@ -10169,6 +10213,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-router": {
|
||||||
|
"version": "6.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.1.tgz",
|
||||||
|
"integrity": "sha512-Jgi8BzAJQ8MkPt8ipXnR73rnD7EmZ0HFFb7jdQU24TynGW1Ooqin2KVDN9voSC+7xhqbbCd2cjGUepb6RObnyg==",
|
||||||
|
"requires": {
|
||||||
|
"@remix-run/router": "1.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"react-router-dom": {
|
||||||
|
"version": "6.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.1.tgz",
|
||||||
|
"integrity": "sha512-67EXNfkQgf34P7+PSb6VlBuaacGhkKn3kpE51+P6zYSG2kiRoumXEL6e27zTa9+PGF2MNXbgIUHTVlleLbIcHQ==",
|
||||||
|
"requires": {
|
||||||
|
"@remix-run/router": "1.3.2",
|
||||||
|
"react-router": "6.8.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-style-singleton": {
|
"react-style-singleton": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
|
||||||
|
|||||||
@@ -35,7 +35,8 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-helmet-async": "^1.3.0",
|
"react-helmet-async": "^1.3.0",
|
||||||
"react-query": "^3.39.3"
|
"react-query": "^3.39.3",
|
||||||
|
"react-router-dom": "^6.8.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^1.2.2",
|
"@tauri-apps/cli": "^1.2.2",
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ pub async fn create_workspace(
|
|||||||
description: &str,
|
description: &str,
|
||||||
pool: &Pool<Sqlite>,
|
pool: &Pool<Sqlite>,
|
||||||
) -> Result<Workspace, sqlx::Error> {
|
) -> Result<Workspace, sqlx::Error> {
|
||||||
let id = generate_id("wrk");
|
let id = generate_id("wk");
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO workspaces (id, name, description)
|
INSERT INTO workspaces (id, name, description)
|
||||||
@@ -106,7 +106,7 @@ pub async fn upsert_request(
|
|||||||
url: &str,
|
url: &str,
|
||||||
pool: &Pool<Sqlite>,
|
pool: &Pool<Sqlite>,
|
||||||
) -> Result<Request, sqlx::Error> {
|
) -> Result<Request, sqlx::Error> {
|
||||||
let id = generate_id("wrk");
|
let id = generate_id("rq");
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO requests (id, workspace_id, name, url, method, body, updated_at, headers)
|
INSERT INTO requests (id, workspace_id, name, url, method, body, updated_at, headers)
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import { Sidebar } from './components/Sidebar';
|
|||||||
import { UrlBar } from './components/UrlBar';
|
import { UrlBar } from './components/UrlBar';
|
||||||
import { Grid } from './components/Grid';
|
import { Grid } from './components/Grid';
|
||||||
import { motion } from 'framer-motion';
|
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 {
|
interface Response {
|
||||||
url: string;
|
url: string;
|
||||||
@@ -21,17 +22,26 @@ interface Response {
|
|||||||
headers: Record<string, string>;
|
headers: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Params = {
|
||||||
|
workspaceId: string;
|
||||||
|
requestId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { data } = useWorkspace();
|
const p = useParams<Params>();
|
||||||
console.log('DATA', data);
|
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 [loading, setLoading] = useState<boolean>(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [response, setResponse] = useState<Response | null>(null);
|
const [response, setResponse] = useState<Response | null>(null);
|
||||||
const [url, setUrl] = useState<string>('https://go-server.schier.dev/debug');
|
const [url, setUrl] = useState<string>(request?.url ?? '');
|
||||||
const [body, setBody] = useState<string>(`{\n "foo": "bar"\n}`);
|
const [body, setBody] = useState<string>(request?.body ?? '');
|
||||||
const [method, setMethod] = useState<{ label: string; value: string }>({
|
const [method, setMethod] = useState<{ label: string; value: string }>({
|
||||||
label: 'GET',
|
label: request?.method ?? 'GET',
|
||||||
value: 'GET',
|
value: request?.method ?? 'GET',
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -44,9 +54,6 @@ function App() {
|
|||||||
return () => document.documentElement.removeEventListener('keypress', listener);
|
return () => document.documentElement.removeEventListener('keypress', listener);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!data) return null;
|
|
||||||
const { requests, workspace } = data;
|
|
||||||
|
|
||||||
async function sendRequest() {
|
async function sendRequest() {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
@@ -73,7 +80,7 @@ function App() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-cols-[auto_1fr] h-full text-gray-900">
|
<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}>
|
<Grid cols={2}>
|
||||||
<VStack className="w-full">
|
<VStack className="w-full">
|
||||||
<HStack as={WindowDragRegion} items="center" className="pl-3 pr-1.5">
|
<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 classnames from 'classnames';
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
|
|
||||||
export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
export interface ButtonProps<T extends ElementType>
|
||||||
|
extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||||
color?: 'primary' | 'secondary';
|
color?: 'primary' | 'secondary';
|
||||||
size?: 'xs' | 'sm' | 'md';
|
size?: 'xs' | 'sm' | 'md';
|
||||||
justify?: 'start' | 'center';
|
justify?: 'start' | 'center';
|
||||||
forDropdown?: boolean;
|
forDropdown?: boolean;
|
||||||
};
|
as?: T;
|
||||||
|
}
|
||||||
|
|
||||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
export function Button<T extends ElementType>({
|
||||||
{
|
className,
|
||||||
className,
|
as,
|
||||||
justify = 'center',
|
justify = 'center',
|
||||||
children,
|
children,
|
||||||
size = 'md',
|
size = 'md',
|
||||||
forDropdown,
|
forDropdown,
|
||||||
color,
|
color,
|
||||||
...props
|
...props
|
||||||
}: ButtonProps,
|
}: ButtonProps<T> & Omit<ComponentPropsWithoutRef<T>, keyof ButtonProps<T>>) {
|
||||||
ref,
|
const Component = as || 'button';
|
||||||
) {
|
|
||||||
return (
|
return (
|
||||||
<button
|
<Component
|
||||||
ref={ref}
|
|
||||||
className={classnames(
|
className={classnames(
|
||||||
className,
|
className,
|
||||||
'rounded-md flex items-center',
|
'rounded-md flex items-center',
|
||||||
@@ -41,6 +41,6 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button
|
|||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
{forDropdown && <Icon icon="triangle-down" className="ml-1 -mr-1" />}
|
{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 * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
||||||
import { DropdownMenuRadioGroup } from '@radix-ui/react-dropdown-menu';
|
import { DropdownMenuRadioGroup } from '@radix-ui/react-dropdown-menu';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import {
|
import { CheckIcon } from '@radix-ui/react-icons';
|
||||||
CheckIcon,
|
import { forwardRef, HTMLAttributes, ReactNode } from 'react';
|
||||||
ChevronRightIcon,
|
|
||||||
DotFilledIcon,
|
|
||||||
HamburgerMenuIcon,
|
|
||||||
} from '@radix-ui/react-icons';
|
|
||||||
import { forwardRef, HTMLAttributes, ReactNode, useState } from 'react';
|
|
||||||
import { Button } from './Button';
|
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { HotKey } from './HotKey';
|
|
||||||
|
|
||||||
interface DropdownMenuRadioProps {
|
interface DropdownMenuRadioProps {
|
||||||
children: ReactNode;
|
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 (
|
return (
|
||||||
<DropdownMenu.Trigger
|
<DropdownMenu.Trigger
|
||||||
asChild
|
asChild
|
||||||
className={classnames(className, 'focus:outline-none')}
|
className={classnames(className, 'focus:outline-none')}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
>
|
||||||
|
<>{children}</>
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
import { forwardRef } from 'react';
|
|
||||||
import { Icon, IconProps } from './Icon';
|
import { Icon, IconProps } from './Icon';
|
||||||
import { Button, ButtonProps } from './Button';
|
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(
|
export function IconButton({ icon, spin, ...props }: Props) {
|
||||||
{ icon, spin, ...props }: Props,
|
|
||||||
ref,
|
|
||||||
) {
|
|
||||||
return (
|
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" />
|
<Icon icon={icon} spin={spin} className="text-gray-700 group-hover:text-gray-900" />
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|||||||
@@ -3,17 +3,19 @@ import classnames from 'classnames';
|
|||||||
import { IconButton } from './IconButton';
|
import { IconButton } from './IconButton';
|
||||||
import { Button } from './Button';
|
import { Button } from './Button';
|
||||||
import useTheme from '../hooks/useTheme';
|
import useTheme from '../hooks/useTheme';
|
||||||
import { HStack } from './Stacks';
|
import { HStack, VStack } from './Stacks';
|
||||||
import { WindowDragRegion } from './WindowDragRegion';
|
import { WindowDragRegion } from './WindowDragRegion';
|
||||||
import { Request } from '../hooks/useWorkspaces';
|
import { Request } from '../hooks/useWorkspaces';
|
||||||
import { invoke } from '@tauri-apps/api';
|
import { invoke } from '@tauri-apps/api';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
requests: Request[];
|
requests: Request[];
|
||||||
|
requestId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Sidebar({ className, workspaceId, requests, ...props }: Props) {
|
export function Sidebar({ className, requestId, workspaceId, requests, ...props }: Props) {
|
||||||
const { toggleTheme } = useTheme();
|
const { toggleTheme } = useTheme();
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -35,11 +37,13 @@ export function Sidebar({ className, workspaceId, requests, ...props }: Props) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</HStack>
|
</HStack>
|
||||||
<ul className="mx-2 py-2">
|
<VStack as="ul" className="py-2" space={1}>
|
||||||
{requests.map((r) => (
|
{requests.map((r) => (
|
||||||
<li key={r.id}>
|
<li key={r.id} className="mx-2">
|
||||||
<Button
|
<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"
|
size="sm"
|
||||||
justify="start"
|
justify="start"
|
||||||
>
|
>
|
||||||
@@ -47,7 +51,7 @@ export function Sidebar({ className, workspaceId, requests, ...props }: Props) {
|
|||||||
</Button>
|
</Button>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</VStack>
|
||||||
</div>
|
</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 () => {
|
return useQuery('requests', async () => {
|
||||||
const workspaces = (await invoke('workspaces')) as Workspace[];
|
const requests = (await invoke('requests', { workspaceId })) as Request[];
|
||||||
const requests = (await invoke('requests', { workspaceId: workspaces[0].id })) as Request[];
|
|
||||||
return requests.map(convertDates);
|
return requests.map(convertDates);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,51 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import init, { greet } from 'hello';
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import { HelmetProvider } from 'react-helmet-async';
|
import { HelmetProvider } from 'react-helmet-async';
|
||||||
import { MotionConfig } from 'framer-motion';
|
import { MotionConfig } from 'framer-motion';
|
||||||
import init, { greet } from 'hello';
|
|
||||||
import { invoke } from '@tauri-apps/api';
|
import { invoke } from '@tauri-apps/api';
|
||||||
import { setTheme } from './lib/theme';
|
import { setTheme } from './lib/theme';
|
||||||
|
|
||||||
import './main.css';
|
|
||||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
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();
|
setTheme();
|
||||||
|
|
||||||
await init();
|
await init();
|
||||||
greet();
|
greet();
|
||||||
await invoke('load_db');
|
await invoke('load_db');
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
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(
|
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<MotionConfig transition={{ duration: 0.15 }}>
|
<MotionConfig transition={{ duration: 0.15 }}>
|
||||||
<HelmetProvider>
|
<HelmetProvider>
|
||||||
<App />
|
<RouterProvider router={router} />
|
||||||
</HelmetProvider>
|
</HelmetProvider>
|
||||||
</MotionConfig>
|
</MotionConfig>
|
||||||
</QueryClientProvider>
|
</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