diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index c9b48bd5..5e155b96 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -340,11 +340,11 @@ async fn create_workspace( async fn create_environment( workspace_id: &str, name: &str, + variables: Vec, window: Window, db_instance: State<'_, Mutex>>, ) -> Result { let pool = &*db_instance.lock().await; - let variables = Vec::new(); let created_environment = models::create_environment(workspace_id, name, variables, pool) .await .expect("Failed to create environment"); diff --git a/src-web/components/DialogContext.tsx b/src-web/components/DialogContext.tsx index 45e68b17..64c67779 100644 --- a/src-web/components/DialogContext.tsx +++ b/src-web/components/DialogContext.tsx @@ -1,8 +1,6 @@ import React, { createContext, useContext, useMemo, useState } from 'react'; import type { DialogProps } from './core/Dialog'; import { Dialog } from './core/Dialog'; -import { useActiveWorkspace } from '../hooks/useActiveWorkspace'; -import { useActiveWorkspaceId } from '../hooks/useActiveWorkspaceId'; type DialogEntry = { id: string; diff --git a/src-web/components/EnvironmentActionsDropdown.tsx b/src-web/components/EnvironmentActionsDropdown.tsx index bffcf21b..e5d7a7f2 100644 --- a/src-web/components/EnvironmentActionsDropdown.tsx +++ b/src-web/components/EnvironmentActionsDropdown.tsx @@ -1,5 +1,5 @@ import classNames from 'classnames'; -import { memo, useMemo } from 'react'; +import { memo, useCallback, useMemo } from 'react'; import { Button } from './core/Button'; import type { DropdownItem } from './core/Dropdown'; import { Dropdown } from './core/Dropdown'; @@ -9,6 +9,8 @@ import { useActiveEnvironment } from '../hooks/useActiveEnvironment'; import { useDialog } from './DialogContext'; import { EnvironmentEditDialog } from './EnvironmentEditDialog'; import { useAppRoutes } from '../hooks/useAppRoutes'; +import { useCreateEnvironment } from '../hooks/useCreateEnvironment'; +import { usePrompt } from '../hooks/usePrompt'; type Props = { className?: string; @@ -19,36 +21,53 @@ export const EnvironmentActionsDropdown = memo(function EnvironmentActionsDropdo }: Props) { const environments = useEnvironments(); const activeEnvironment = useActiveEnvironment(); + const createEnvironment = useCreateEnvironment(); const dialog = useDialog(); + const prompt = usePrompt(); const routes = useAppRoutes(); + const showEnvironmentDialog = useCallback(() => { + dialog.show({ + title: "Manage Environments", + render: () => , + }); + }, [dialog]); + const items: DropdownItem[] = useMemo( - () => [ - ...environments.map( - (e) => ({ - key: e.id, - label: e.name, - rightSlot: e.id === activeEnvironment?.id ? : undefined, - onSelect: async () => { - routes.setEnvironment(e); - }, - }), - [activeEnvironment?.id], - ), - { type: 'separator', label: 'Environments' }, - { - key: 'edit', - label: 'Manage Environments', - leftSlot: , - onSelect: async () => { - dialog.show({ - title: 'Environments', - render: () => , - }); - }, - }, - ], - [activeEnvironment, dialog, environments, routes], + () => + environments.length === 0 + ? [ + { + key: 'create', + label: 'Create Environment', + leftSlot: , + onSelect: async () => { + await createEnvironment.mutateAsync(); + showEnvironmentDialog(); + }, + }, + ] + : [ + ...environments.map( + (e) => ({ + key: e.id, + label: e.name, + rightSlot: e.id === activeEnvironment?.id ? : undefined, + onSelect: async () => { + routes.setEnvironment(e); + }, + }), + [activeEnvironment?.id], + ), + { type: 'separator', label: 'Environments' }, + { + key: 'edit', + label: 'Manage Environments', + leftSlot: , + onSelect: showEnvironmentDialog, + }, + ], + [activeEnvironment, environments, routes, prompt, createEnvironment, showEnvironmentDialog], ); return ( diff --git a/src-web/components/EnvironmentEditDialog.tsx b/src-web/components/EnvironmentEditDialog.tsx index b547bdeb..9fc92fab 100644 --- a/src-web/components/EnvironmentEditDialog.tsx +++ b/src-web/components/EnvironmentEditDialog.tsx @@ -1,6 +1,5 @@ import { useCreateEnvironment } from '../hooks/useCreateEnvironment'; import { useEnvironments } from '../hooks/useEnvironments'; -import { usePrompt } from '../hooks/usePrompt'; import type { Environment } from '../lib/models'; import { Button } from './core/Button'; import classNames from 'classnames'; @@ -8,22 +7,25 @@ import { useActiveEnvironment } from '../hooks/useActiveEnvironment'; import { useAppRoutes } from '../hooks/useAppRoutes'; import { PairEditor } from './core/PairEditor'; import type { PairEditorProps } from './core/PairEditor'; -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import { useUpdateEnvironment } from '../hooks/useUpdateEnvironment'; +import { HStack, VStack } from './core/Stacks'; +import { IconButton } from './core/IconButton'; +import { useDeleteEnvironment } from '../hooks/useDeleteEnvironment'; +import type { GenericCompletionConfig } from './core/Editor/genericCompletion'; export const EnvironmentEditDialog = function () { const routes = useAppRoutes(); - const prompt = usePrompt(); const environments = useEnvironments(); const createEnvironment = useCreateEnvironment(); const activeEnvironment = useActiveEnvironment(); return (
- + {activeEnvironment != null && }
); }; const EnvironmentEditor = function ({ environment }: { environment: Environment }) { + const environments = useEnvironments(); const updateEnvironment = useUpdateEnvironment(environment.id); + const deleteEnvironment = useDeleteEnvironment(environment); const handleChange = useCallback( (variables) => { updateEnvironment.mutate({ variables }); }, [updateEnvironment], ); + + const nameAutocomplete = useMemo(() => { + const otherVariableNames = environments.flatMap((e) => e.variables.map((v) => v.name)); + const variableNames = otherVariableNames.filter( + (name) => !environment.variables.some((v) => v.name === name), + ); + return { options: variableNames.map((name) => ({ label: name, type: 'constant' })) }; + }, [environments, environment.variables]); + return ( -
+ + +

{environment.name}

+ deleteEnvironment.mutate()} + /> +
-
+ ); }; diff --git a/src-web/components/HeaderEditor.tsx b/src-web/components/HeaderEditor.tsx index 04c83637..9e42c441 100644 --- a/src-web/components/HeaderEditor.tsx +++ b/src-web/components/HeaderEditor.tsx @@ -17,6 +17,8 @@ type Props = { export function HeaderEditor({ headers, onChange, forceUpdateKey }: Props) { return (