mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-24 01:28:35 +02:00
Started on environment edit dialog
This commit is contained in:
@@ -343,6 +343,22 @@ async fn create_workspace(
|
|||||||
emit_and_return(&window, "created_model", created_workspace)
|
emit_and_return(&window, "created_model", created_workspace)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn create_environment(
|
||||||
|
workspace_id: &str,
|
||||||
|
name: &str,
|
||||||
|
window: Window<Wry>,
|
||||||
|
db_instance: State<'_, Mutex<Pool<Sqlite>>>,
|
||||||
|
) -> Result<models::Environment, String> {
|
||||||
|
let pool = &*db_instance.lock().await;
|
||||||
|
let data: HashMap<String, JsonValue> = HashMap::new();
|
||||||
|
let created_environment = models::create_environment(workspace_id, name, data, pool)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create environment");
|
||||||
|
|
||||||
|
emit_and_return(&window, "created_model", created_environment)
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn create_request(
|
async fn create_request(
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
@@ -639,6 +655,7 @@ fn main() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
create_environment,
|
||||||
create_request,
|
create_request,
|
||||||
create_workspace,
|
create_workspace,
|
||||||
delete_all_responses,
|
delete_all_responses,
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { DndProvider } from 'react-dnd';
|
|||||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||||
import { HelmetProvider } from 'react-helmet-async';
|
import { HelmetProvider } from 'react-helmet-async';
|
||||||
import { AppRouter } from './AppRouter';
|
import { AppRouter } from './AppRouter';
|
||||||
import { DialogProvider } from './DialogContext';
|
|
||||||
|
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
logger: undefined,
|
logger: undefined,
|
||||||
@@ -24,12 +23,10 @@ export function App() {
|
|||||||
<MotionConfig transition={{ duration: 0.1 }}>
|
<MotionConfig transition={{ duration: 0.1 }}>
|
||||||
<HelmetProvider>
|
<HelmetProvider>
|
||||||
<DndProvider backend={HTML5Backend}>
|
<DndProvider backend={HTML5Backend}>
|
||||||
<DialogProvider>
|
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<AppRouter />
|
<AppRouter />
|
||||||
{/*<ReactQueryDevtools initialIsOpen={false} />*/}
|
{/*<ReactQueryDevtools initialIsOpen={false} />*/}
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</DialogProvider>
|
|
||||||
</DndProvider>
|
</DndProvider>
|
||||||
</HelmetProvider>
|
</HelmetProvider>
|
||||||
</MotionConfig>
|
</MotionConfig>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { GlobalHooks } from './GlobalHooks';
|
|||||||
import RouteError from './RouteError';
|
import RouteError from './RouteError';
|
||||||
import Workspace from './Workspace';
|
import Workspace from './Workspace';
|
||||||
import Workspaces from './Workspaces';
|
import Workspaces from './Workspaces';
|
||||||
|
import { DialogProvider } from './DialogContext';
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
@@ -59,9 +60,9 @@ function WorkspaceOrRedirect() {
|
|||||||
|
|
||||||
function Layout() {
|
function Layout() {
|
||||||
return (
|
return (
|
||||||
<>
|
<DialogProvider>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
<GlobalHooks />
|
<GlobalHooks />
|
||||||
</>
|
</DialogProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import React, { createContext, useContext, useMemo, useState } from 'react';
|
import React, { createContext, useContext, useMemo, useState } from 'react';
|
||||||
import type { DialogProps } from './core/Dialog';
|
import type { DialogProps } from './core/Dialog';
|
||||||
import { Dialog } from './core/Dialog';
|
import { Dialog } from './core/Dialog';
|
||||||
|
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
||||||
|
import { useActiveWorkspaceId } from '../hooks/useActiveWorkspaceId';
|
||||||
|
|
||||||
type DialogEntry = {
|
type DialogEntry = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -54,9 +56,11 @@ export const DialogProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
|
|
||||||
function DialogInstance({ id, render, ...props }: DialogEntry) {
|
function DialogInstance({ id, render, ...props }: DialogEntry) {
|
||||||
const { actions } = useContext(DialogContext);
|
const { actions } = useContext(DialogContext);
|
||||||
|
const children = render({ hide: () => actions.hide(id) });
|
||||||
|
console.log("ACITEV WORKSPAXCE ID 2", useActiveWorkspaceId());
|
||||||
return (
|
return (
|
||||||
<Dialog open onClose={() => actions.hide(id)} {...props}>
|
<Dialog open onClose={() => actions.hide(id)} {...props}>
|
||||||
{render({ hide: () => actions.hide(id) })}
|
{children}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
69
src-web/components/EnvironmentEditDialog.tsx
Normal file
69
src-web/components/EnvironmentEditDialog.tsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { useCreateEnvironment } from '../hooks/useCreateEnvironment';
|
||||||
|
import { useEnvironments } from '../hooks/useEnvironments';
|
||||||
|
import { usePrompt } from '../hooks/usePrompt';
|
||||||
|
import { useUpdateEnvironment } from '../hooks/useUpdateEnvironment';
|
||||||
|
import type { Environment } from '../lib/models';
|
||||||
|
import { Button } from './core/Button';
|
||||||
|
import { Editor } from './core/Editor';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
export const EnvironmentEditDialog = function () {
|
||||||
|
const prompt = usePrompt();
|
||||||
|
const environments = useEnvironments();
|
||||||
|
const createEnvironment = useCreateEnvironment();
|
||||||
|
const [activeEnvironment, setActiveEnvironment] = useState<Environment | null>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full grid gap-3 grid-cols-[auto_minmax(0,1fr)]">
|
||||||
|
<aside className="h-full min-w-[120px] pr-3 border-r border-gray-200">
|
||||||
|
{environments.map((e) => (
|
||||||
|
<Button
|
||||||
|
className={classnames('w-full', activeEnvironment?.id === e.id && 'bg-highlight')}
|
||||||
|
justify="start"
|
||||||
|
key={e.id}
|
||||||
|
onClick={() => {
|
||||||
|
setActiveEnvironment(e);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{e.name}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
<Button
|
||||||
|
color="gray"
|
||||||
|
onClick={async () => {
|
||||||
|
const name = await prompt({
|
||||||
|
title: 'Environment Name',
|
||||||
|
defaultValue: 'My Env',
|
||||||
|
label: 'Name',
|
||||||
|
name: 'environment',
|
||||||
|
});
|
||||||
|
createEnvironment.mutate({ name });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Create Environment
|
||||||
|
</Button>
|
||||||
|
</aside>
|
||||||
|
{activeEnvironment != null && <EnvironmentEditor environment={activeEnvironment} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const EnvironmentEditor = function ({ environment }: { environment: Environment }) {
|
||||||
|
const updateEnvironment = useUpdateEnvironment(environment.id);
|
||||||
|
return (
|
||||||
|
<Editor
|
||||||
|
contentType="application/json"
|
||||||
|
className="w-full min-h-[40px] !bg-gray-50"
|
||||||
|
defaultValue={JSON.stringify(environment.data, null, 2)}
|
||||||
|
forceUpdateKey={environment.id}
|
||||||
|
onChange={(data) => {
|
||||||
|
try {
|
||||||
|
updateEnvironment.mutate({ data: JSON.parse(data) });
|
||||||
|
} catch (err) {
|
||||||
|
// That's okay
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -40,7 +40,7 @@ export function Overlay({ zIndex = 30, open, onClose, portalName, children }: Pr
|
|||||||
{children}
|
{children}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
)}
|
)}
|
||||||
</Portal>
|
</Portal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,17 +9,13 @@ import { SidebarActions } from './SidebarActions';
|
|||||||
import { WorkspaceActionsDropdown } from './WorkspaceActionsDropdown';
|
import { WorkspaceActionsDropdown } from './WorkspaceActionsDropdown';
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
import { useDialog } from './DialogContext';
|
import { useDialog } from './DialogContext';
|
||||||
import { useEnvironments } from '../hooks/useEnvironments';
|
import { EnvironmentEditDialog } from './EnvironmentEditDialog';
|
||||||
import type { Environment } from '../lib/models';
|
|
||||||
import { Editor } from './core/Editor';
|
|
||||||
import { useUpdateEnvironment } from '../hooks/useUpdateEnvironment';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Props) {
|
export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Props) {
|
||||||
const environments = useEnvironments();
|
|
||||||
const activeRequest = useActiveRequest();
|
const activeRequest = useActiveRequest();
|
||||||
const dialog = useDialog();
|
const dialog = useDialog();
|
||||||
|
|
||||||
@@ -35,14 +31,8 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
|
|||||||
<Button onClick={() => {
|
<Button onClick={() => {
|
||||||
dialog.show({
|
dialog.show({
|
||||||
title: 'Environments',
|
title: 'Environments',
|
||||||
render: () => <div>
|
size: 'full',
|
||||||
{environments.map(e => (
|
render: () => <EnvironmentEditDialog />,
|
||||||
<EnvironmentList
|
|
||||||
key={e.id}
|
|
||||||
environment={e}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
})
|
})
|
||||||
}}>
|
}}>
|
||||||
Environments
|
Environments
|
||||||
@@ -66,28 +56,3 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
|
|||||||
</HStack>
|
</HStack>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
interface EnvironmentListProps {
|
|
||||||
environment: Environment;
|
|
||||||
}
|
|
||||||
|
|
||||||
const EnvironmentList = function({ environment }: EnvironmentListProps) {
|
|
||||||
const updateEnvironment = useUpdateEnvironment(environment.id)
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1>{environment.name}</h1>
|
|
||||||
<Editor
|
|
||||||
contentType="application/json"
|
|
||||||
className='w-full h-[400px] !bg-gray-50'
|
|
||||||
defaultValue={JSON.stringify(environment.data, null, 2)}
|
|
||||||
onChange={data => {
|
|
||||||
try {
|
|
||||||
updateEnvironment.mutate({ data: JSON.parse(data) });
|
|
||||||
} catch (err) {
|
|
||||||
// That's okay
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -54,11 +54,11 @@ export function Dialog({
|
|||||||
className={classnames(
|
className={classnames(
|
||||||
className,
|
className,
|
||||||
'relative bg-gray-50 pointer-events-auto',
|
'relative bg-gray-50 pointer-events-auto',
|
||||||
'max-h-[80vh] p-5 rounded-lg overflow-auto',
|
'p-5 rounded-lg overflow-auto',
|
||||||
'dark:border border-highlight shadow shadow-black/10',
|
'dark:border border-highlight shadow shadow-black/10',
|
||||||
size === 'sm' && 'w-[25rem]',
|
size === 'sm' && 'w-[25rem] max-h-[80vh]',
|
||||||
size === 'md' && 'w-[45rem]',
|
size === 'md' && 'w-[45rem] max-h-[80vh]',
|
||||||
size === 'full' && 'w-[80vw]',
|
size === 'full' && 'w-[calc(100vw-8em)] h-[calc(100vh-8em)]',
|
||||||
size === 'dynamic' && 'min-w-[30vw] max-w-[80vw]',
|
size === 'dynamic' && 'min-w-[30vw] max-w-[80vw]',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -66,7 +66,7 @@ export function Dialog({
|
|||||||
{title}
|
{title}
|
||||||
</Heading>
|
</Heading>
|
||||||
{description && <p id={descriptionId}>{description}</p>}
|
{description && <p id={descriptionId}>{description}</p>}
|
||||||
<div className="mt-4">{children}</div>
|
<div className="h-full w-full mt-4">{children}</div>
|
||||||
|
|
||||||
{/*Put close at the end so that it's the last thing to be tabbed to*/}
|
{/*Put close at the end so that it's the last thing to be tabbed to*/}
|
||||||
{!hideX && (
|
{!hideX && (
|
||||||
|
|||||||
22
src-web/hooks/useCreateEnvironment.ts
Normal file
22
src-web/hooks/useCreateEnvironment.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
import { invoke } from '@tauri-apps/api';
|
||||||
|
import type { Environment } from '../lib/models';
|
||||||
|
import { environmentsQueryKey } from './useEnvironments';
|
||||||
|
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
|
||||||
|
|
||||||
|
export function useCreateEnvironment() {
|
||||||
|
const workspaceId = useActiveWorkspaceId();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation<Environment, unknown, Pick<Environment, 'name'>>({
|
||||||
|
mutationFn: (patch) => {
|
||||||
|
return invoke('create_environment', { ...patch, workspaceId });
|
||||||
|
},
|
||||||
|
onSuccess: async (environment) => {
|
||||||
|
if (workspaceId == null) return;
|
||||||
|
queryClient.setQueryData<Environment[]>(environmentsQueryKey({ workspaceId }), (environments) => [
|
||||||
|
...(environments ?? []),
|
||||||
|
environment,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user