Move workspace menu, better env mgmt, QoL

This commit is contained in:
Gregory Schier
2023-10-29 09:45:16 -07:00
parent b1764d0508
commit 4d93892249
19 changed files with 214 additions and 104 deletions

View File

@@ -340,11 +340,11 @@ async fn create_workspace(
async fn create_environment(
workspace_id: &str,
name: &str,
variables: Vec<models::EnvironmentVariable>,
window: Window<Wry>,
db_instance: State<'_, Mutex<Pool<Sqlite>>>,
) -> Result<models::Environment, String> {
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");

View File

@@ -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;

View File

@@ -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: () => <EnvironmentEditDialog />,
});
}, [dialog]);
const items: DropdownItem[] = useMemo(
() => [
...environments.map(
(e) => ({
key: e.id,
label: e.name,
rightSlot: e.id === activeEnvironment?.id ? <Icon icon="check" /> : undefined,
onSelect: async () => {
routes.setEnvironment(e);
},
}),
[activeEnvironment?.id],
),
{ type: 'separator', label: 'Environments' },
{
key: 'edit',
label: 'Manage Environments',
leftSlot: <Icon icon="gear" />,
onSelect: async () => {
dialog.show({
title: 'Environments',
render: () => <EnvironmentEditDialog />,
});
},
},
],
[activeEnvironment, dialog, environments, routes],
() =>
environments.length === 0
? [
{
key: 'create',
label: 'Create Environment',
leftSlot: <Icon icon="plusCircle" />,
onSelect: async () => {
await createEnvironment.mutateAsync();
showEnvironmentDialog();
},
},
]
: [
...environments.map(
(e) => ({
key: e.id,
label: e.name,
rightSlot: e.id === activeEnvironment?.id ? <Icon icon="check" /> : undefined,
onSelect: async () => {
routes.setEnvironment(e);
},
}),
[activeEnvironment?.id],
),
{ type: 'separator', label: 'Environments' },
{
key: 'edit',
label: 'Manage Environments',
leftSlot: <Icon icon="gear" />,
onSelect: showEnvironmentDialog,
},
],
[activeEnvironment, environments, routes, prompt, createEnvironment, showEnvironmentDialog],
);
return (

View File

@@ -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 (
<div className="h-full grid gap-3 grid-cols-[auto_minmax(0,1fr)]">
<aside className="relative h-full min-w-[200px] pr-3 border-r border-gray-200">
<VStack space={0.5} className="relative h-full min-w-[200px] pr-3 border-r border-gray-100">
{environments.map((e) => (
<Button
size="sm"
size="xs"
className={classNames(
'w-full',
activeEnvironment?.id === e.id && 'bg-gray-100 text-gray-1000',
@@ -41,39 +43,55 @@ export const EnvironmentEditDialog = function () {
size="sm"
className="mr-5 absolute bottom-0 left-0 right-0"
color="gray"
onClick={async () => {
const name = await prompt({
title: 'Environment Name',
defaultValue: 'My Env',
label: 'Name',
name: 'environment',
});
createEnvironment.mutate({ name });
}}
onClick={() => createEnvironment.mutate()}
>
New Environment
</Button>
</aside>
</VStack>
{activeEnvironment != null && <EnvironmentEditor environment={activeEnvironment} />}
</div>
);
};
const EnvironmentEditor = function ({ environment }: { environment: Environment }) {
const environments = useEnvironments();
const updateEnvironment = useUpdateEnvironment(environment.id);
const deleteEnvironment = useDeleteEnvironment(environment);
const handleChange = useCallback<PairEditorProps['onChange']>(
(variables) => {
updateEnvironment.mutate({ variables });
},
[updateEnvironment],
);
const nameAutocomplete = useMemo<GenericCompletionConfig>(() => {
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 (
<div>
<VStack space={2}>
<HStack space={2} className="justify-between">
<h1 className="text-xl">{environment.name}</h1>
<IconButton
icon="trash"
title="Delete Environment"
size="sm"
className="!h-auto w-8"
onClick={() => deleteEnvironment.mutate()}
/>
</HStack>
<PairEditor
nameAutocomplete={nameAutocomplete}
nameAutocompleteVariables={false}
valueAutocompleteVariables={false}
forceUpdateKey={environment.id}
pairs={environment.variables}
onChange={handleChange}
/>
</div>
</VStack>
);
};

View File

@@ -17,6 +17,8 @@ type Props = {
export function HeaderEditor({ headers, onChange, forceUpdateKey }: Props) {
return (
<PairEditor
valueAutocompleteVariables
nameAutocompleteVariables
pairs={headers}
onChange={onChange}
forceUpdateKey={forceUpdateKey}

View File

@@ -16,13 +16,11 @@ import { useUpdateRequest } from '../hooks/useUpdateRequest';
import type { HttpRequest } from '../lib/models';
import { isResponseLoading } from '../lib/models';
import { Icon } from './core/Icon';
import { HStack, VStack } from './core/Stacks';
import { StatusTag } from './core/StatusTag';
import { DropMarker } from './DropMarker';
import { useActiveEnvironmentId } from '../hooks/useActiveEnvironmentId';
import { WorkspaceActionsDropdown } from './WorkspaceActionsDropdown';
import { IconButton } from './core/IconButton';
import { useCreateRequest } from '../hooks/useCreateRequest';
import { VStack } from './core/Stacks';
interface Props {
className?: string;
@@ -34,7 +32,7 @@ enum ItemTypes {
export const Sidebar = memo(function Sidebar({ className }: Props) {
const { hidden } = useSidebarHidden();
const createRequest = useCreateRequest({ navigateAfter: true });
const createRequest = useCreateRequest();
const sidebarRef = useRef<HTMLDivElement>(null);
const activeRequestId = useActiveRequestId();
const activeEnvironmentId = useActiveEnvironmentId();
@@ -156,15 +154,6 @@ export const Sidebar = memo(function Sidebar({ className }: Props) {
return (
<div aria-hidden={hidden} className="h-full grid grid-rows-[auto_minmax(0,1fr)]">
<HStack className="mt-1 pt-1 mx-2" justifyContent="between" alignItems="center" space={1}>
<WorkspaceActionsDropdown forDropdown={false} className="text-left mb-0" justify="start" />
<IconButton
size="sm"
icon="plusCircle"
title="Create Request"
onClick={() => createRequest.mutate({})}
/>
</HStack>
<div
role="menu"
aria-orientation="vertical"

View File

@@ -1,11 +1,22 @@
import { memo } from 'react';
import { useSidebarHidden } from '../hooks/useSidebarHidden';
import { IconButton } from './core/IconButton';
import { useCreateRequest } from '../hooks/useCreateRequest';
export const SidebarActions = memo(function SidebarActions() {
const createRequest = useCreateRequest();
const { hidden, toggle } = useSidebarHidden();
if (!hidden) return null;
return (
<div>
<IconButton
size="sm"
icon="plusCircle"
title="Create Request"
onClick={() => createRequest.mutate({})}
/>
<IconButton
onClick={toggle}
className="pointer-events-auto"
@@ -13,5 +24,6 @@ export const SidebarActions = memo(function SidebarActions() {
title="Show sidebar"
icon={hidden ? 'leftPanelHidden' : 'leftPanelVisible'}
/>
</div>
);
});

View File

@@ -47,6 +47,7 @@ export const UrlBar = memo(function UrlBar({ id: requestId, url, method, classNa
return (
<form onSubmit={handleSubmit} className={classNames('url-bar', className)}>
<Input
autocompleteVariables
ref={inputRef}
size={isFocused ? 'auto' : 'sm'}
hideLabel

View File

@@ -29,7 +29,7 @@ const drag = { gridArea: 'drag' };
export default function Workspace() {
const { setWidth, width, resetWidth } = useSidebarWidth();
const { hide, hidden, toggle } = useSidebarHidden();
const { hide, show, hidden, toggle } = useSidebarHidden();
const windowSize = useWindowSize();
const [floating, setFloating] = useState<boolean>(false);
@@ -64,7 +64,14 @@ export default function Workspace() {
moveState.current = {
move: async (e: MouseEvent) => {
e.preventDefault(); // Prevent text selection and things
setWidth(startWidth + (e.clientX - mouseStartX));
const newWidth = startWidth + (e.clientX - mouseStartX);
if (newWidth < 100) {
hide();
resetWidth();
} else {
show();
setWidth(newWidth);
}
},
up: (e: MouseEvent) => {
e.preventDefault();

View File

@@ -153,12 +153,9 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
return (
<Dropdown items={items}>
<Button
forDropdown
size="sm"
className={classNames(className, 'text-gray-800 !px-2 truncate')}
forDropdown
leftSlot={
<img src="https://yaak.app/logo.svg" alt="Workspace logo" className="w-4 h-4 mr-1" />
}
{...buttonProps}
>
{activeWorkspace?.name}

View File

@@ -7,6 +7,7 @@ import { RecentRequestsDropdown } from './RecentRequestsDropdown';
import { RequestActionsDropdown } from './RequestActionsDropdown';
import { SidebarActions } from './SidebarActions';
import { EnvironmentActionsDropdown } from './EnvironmentActionsDropdown';
import { WorkspaceActionsDropdown } from './WorkspaceActionsDropdown';
interface Props {
className?: string;
@@ -23,6 +24,7 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
>
<HStack space={0.5} className="flex-1 pointer-events-none" alignItems="center">
<SidebarActions />
<WorkspaceActionsDropdown />
<EnvironmentActionsDropdown className="pointer-events-auto" />
</HStack>
<div className="pointer-events-none">

View File

@@ -4,12 +4,13 @@ import { Icon } from './Icon';
interface Props {
checked: boolean;
title: string;
onChange: (checked: boolean) => void;
disabled?: boolean;
className?: string;
}
export function Checkbox({ checked, onChange, className, disabled }: Props) {
export function Checkbox({ checked, onChange, className, disabled, title }: Props) {
const handleClick = useCallback(() => {
onChange(!checked);
}, [onChange, checked]);
@@ -20,6 +21,7 @@ export function Checkbox({ checked, onChange, className, disabled }: Props) {
aria-checked={checked ? 'true' : 'false'}
disabled={disabled}
onClick={handleClick}
title={title}
className={classNames(
className,
'flex-shrink-0 w-4 h-4 border border-gray-200 rounded',

View File

@@ -11,7 +11,7 @@ export interface DialogProps {
children: ReactNode;
open: boolean;
onClose: () => void;
title: ReactNode;
title?: ReactNode;
description?: ReactNode;
className?: string;
size?: 'sm' | 'md' | 'full' | 'dynamic';
@@ -63,9 +63,13 @@ export function Dialog({
size === 'dynamic' && 'min-w-[30vw] max-w-[80vw]',
)}
>
<Heading className="text-xl font-semibold w-full" id={titleId}>
{title}
</Heading>
{title ? (
<Heading className="text-xl font-semibold w-full" id={titleId}>
{title}
</Heading>
) : (
<span />
)}
{description && <p id={descriptionId}>{description}</p>}
<div className="h-full w-full">{children}</div>
{/*Put close at the end so that it's the last thing to be tabbed to*/}

View File

@@ -41,6 +41,7 @@ export interface EditorProps {
wrapLines?: boolean;
format?: (v: string) => string;
autocomplete?: GenericCompletionConfig;
autocompleteVariables?: boolean;
actions?: ReactNode;
}
@@ -64,12 +65,14 @@ const _Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
singleLine,
format,
autocomplete,
autocompleteVariables,
actions,
wrapLines,
}: EditorProps,
ref,
) {
const environment = useActiveEnvironment();
const e = useActiveEnvironment();
const environment = autocompleteVariables ? e : null;
const cm = useRef<{ view: EditorView; languageCompartment: Compartment } | null>(null);
useImperativeHandle(ref, () => cm.current?.view);

View File

@@ -37,7 +37,6 @@ import { text } from './text/extension';
import { twig } from './twig/extension';
import { url } from './url/extension';
import type { Environment } from '../../../lib/models';
import { EditorView } from 'codemirror';
export const myHighlightStyle = HighlightStyle.define([
{

View File

@@ -8,7 +8,7 @@ import { IconButton } from './IconButton';
import { HStack, VStack } from './Stacks';
export type InputProps = Omit<HTMLAttributes<HTMLInputElement>, 'onChange' | 'onFocus'> &
Pick<EditorProps, 'contentType' | 'useTemplating' | 'autocomplete' | 'forceUpdateKey' | 'autoFocus' | 'autoSelect'> & {
Pick<EditorProps, 'contentType' | 'useTemplating' | 'autocomplete' | 'forceUpdateKey' | 'autoFocus' | 'autoSelect' | 'autocompleteVariables'> & {
name: string;
type?: 'text' | 'password';
label: string;

View File

@@ -10,6 +10,7 @@ import { Icon } from './Icon';
import { IconButton } from './IconButton';
import type { InputProps } from './Input';
import { Input } from './Input';
import type { EditorView } from 'codemirror';
export type PairEditorProps = {
pairs: Pair[];
@@ -20,6 +21,8 @@ export type PairEditorProps = {
valuePlaceholder?: string;
nameAutocomplete?: GenericCompletionConfig;
valueAutocomplete?: (name: string) => GenericCompletionConfig | undefined;
nameAutocompleteVariables?: boolean;
valueAutocompleteVariables?: boolean;
nameValidate?: InputProps['validate'];
valueValidate?: InputProps['validate'];
};
@@ -37,17 +40,20 @@ type PairContainer = {
};
export const PairEditor = memo(function PairEditor({
pairs: originalPairs,
className,
forceUpdateKey,
nameAutocomplete,
valueAutocomplete,
nameAutocompleteVariables,
namePlaceholder,
valuePlaceholder,
nameValidate,
valueValidate,
className,
onChange,
pairs: originalPairs,
valueAutocomplete,
valueAutocompleteVariables,
valuePlaceholder,
valueValidate,
}: PairEditorProps) {
const [forceFocusPairId, setForceFocusPairId] = useState<string | null>(null);
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
const [pairs, setPairs] = useState<PairContainer[]>(() => {
// Remove empty headers on initial render
@@ -105,6 +111,15 @@ export const PairEditor = memo(function PairEditor({
[hoveredIndex, setPairsAndSave],
);
const handleSubmitRow = useCallback(
(pair: PairContainer) => {
const index = pairs.findIndex((p) => p.id === pair.id);
const id = pairs[index + 1]?.id ?? null;
setForceFocusPairId(id);
},
[pairs],
);
const handleChange = useCallback(
(pair: PairContainer) =>
setPairsAndSave((pairs) => pairs.map((p) => (pair.id !== p.id ? p : pair))),
@@ -152,6 +167,9 @@ export const PairEditor = memo(function PairEditor({
pairContainer={p}
className="py-1"
isLast={isLast}
nameAutocompleteVariables={nameAutocompleteVariables}
valueAutocompleteVariables={valueAutocompleteVariables}
forceFocusPairId={forceFocusPairId}
forceUpdateKey={forceUpdateKey}
nameAutocomplete={nameAutocomplete}
valueAutocomplete={valueAutocomplete}
@@ -159,6 +177,7 @@ export const PairEditor = memo(function PairEditor({
valuePlaceholder={valuePlaceholder}
nameValidate={nameValidate}
valueValidate={valueValidate}
onSubmit={handleSubmitRow}
onChange={handleChange}
onFocus={handleFocus}
onDelete={isLast ? undefined : handleDelete}
@@ -179,16 +198,20 @@ enum ItemTypes {
type FormRowProps = {
className?: string;
pairContainer: PairContainer;
forceFocusPairId?: string | null;
onMove: (id: string, side: 'above' | 'below') => void;
onEnd: (id: string) => void;
onChange: (pair: PairContainer) => void;
onDelete?: (pair: PairContainer) => void;
onFocus?: (pair: PairContainer) => void;
onSubmit?: (pair: PairContainer) => void;
isLast?: boolean;
} & Pick<
PairEditorProps,
| 'nameAutocomplete'
| 'valueAutocomplete'
| 'nameAutocompleteVariables'
| 'valueAutocompleteVariables'
| 'namePlaceholder'
| 'valuePlaceholder'
| 'nameValidate'
@@ -198,23 +221,34 @@ type FormRowProps = {
const FormRow = memo(function FormRow({
className,
pairContainer,
forceFocusPairId,
forceUpdateKey,
isLast,
nameAutocomplete,
namePlaceholder,
nameAutocompleteVariables,
valueAutocompleteVariables,
nameValidate,
onChange,
onDelete,
onEnd,
onFocus,
onMove,
onEnd,
isLast,
forceUpdateKey,
nameAutocomplete,
onSubmit,
pairContainer,
valueAutocomplete,
namePlaceholder,
valuePlaceholder,
nameValidate,
valueValidate,
}: FormRowProps) {
const { id } = pairContainer;
const ref = useRef<HTMLDivElement>(null);
const nameInputRef = useRef<EditorView>(null);
useEffect(() => {
if (forceFocusPairId === pairContainer.id) {
nameInputRef.current?.focus();
}
}, [forceFocusPairId, pairContainer.id]);
const handleChangeEnabled = useMemo(
() => (enabled: boolean) => onChange({ id, pair: { ...pairContainer.pair, enabled } }),
@@ -237,7 +271,7 @@ const FormRow = memo(function FormRow({
const [, connectDrop] = useDrop<PairContainer>(
{
accept: ItemTypes.ROW,
hover: (item, monitor) => {
hover: (_, monitor) => {
if (!ref.current) return;
const hoverBoundingRect = ref.current?.getBoundingClientRect();
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
@@ -285,12 +319,18 @@ const FormRow = memo(function FormRow({
<span className="w-3" />
)}
<Checkbox
title={pairContainer.pair.enabled ? 'disable entry' : 'Enable item'}
disabled={isLast}
checked={isLast ? false : !!pairContainer.pair.enabled}
className={classNames('mr-2', isLast && '!opacity-disabled')}
onChange={handleChangeEnabled}
/>
<div
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
onSubmit?.(pairContainer);
}}
className={classNames(
'grid items-center',
'@xs:gap-2 @xs:!grid-rows-1 @xs:!grid-cols-[minmax(0,1fr)_minmax(0,1fr)]',
@@ -298,11 +338,12 @@ const FormRow = memo(function FormRow({
)}
>
<Input
ref={nameInputRef}
hideLabel
useTemplating
size="sm"
require={!isLast && !!pairContainer.pair.enabled && !!pairContainer.pair.value}
validate={nameValidate}
useTemplating
forceUpdateKey={forceUpdateKey}
containerClassName={classNames(isLast && 'border-dashed')}
defaultValue={pairContainer.pair.name}
@@ -312,9 +353,11 @@ const FormRow = memo(function FormRow({
onFocus={handleFocus}
placeholder={namePlaceholder ?? 'name'}
autocomplete={nameAutocomplete}
autocompleteVariables={nameAutocompleteVariables}
/>
<Input
hideLabel
useTemplating
size="sm"
containerClassName={classNames(isLast && 'border-dashed')}
validate={valueValidate}
@@ -325,10 +368,10 @@ const FormRow = memo(function FormRow({
onChange={handleChangeValue}
onFocus={handleFocus}
placeholder={valuePlaceholder ?? 'value'}
useTemplating
autocomplete={valueAutocomplete?.(pairContainer.pair.name)}
autocompleteVariables={valueAutocompleteVariables}
/>
</div>
</form>
<IconButton
aria-hidden={!onDelete}
disabled={!onDelete}

View File

@@ -1,18 +1,34 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { invoke } from '@tauri-apps/api';
import type { Environment } from '../lib/models';
import { environmentsQueryKey } from './useEnvironments';
import { environmentsQueryKey, useEnvironments } from './useEnvironments';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useAppRoutes } from './useAppRoutes';
import { usePrompt } from './usePrompt';
import { useWorkspaces } from './useWorkspaces';
export function useCreateEnvironment() {
const routes = useAppRoutes();
const prompt = usePrompt();
const workspaceId = useActiveWorkspaceId();
const queryClient = useQueryClient();
const routes = useAppRoutes();
const environments = useEnvironments();
const workspaces = useWorkspaces();
return useMutation<Environment, unknown, Pick<Environment, 'name'>>({
mutationFn: (patch) => {
return invoke('create_environment', { ...patch, workspaceId });
return useMutation<Environment, unknown, void>({
mutationFn: async () => {
const name = await prompt({
name: 'name',
title: 'Create Environment',
description: 'Enter a name for the new environment',
label: 'Name',
defaultValue: 'My Environment',
});
const variables =
environments.length === 0 && workspaces.length === 1
? [{ name: 'first_variable', value: 'some reusable value' }]
: [];
return invoke('create_environment', { name, variables, workspaceId });
},
onSuccess: async (environment) => {
if (workspaceId == null) return;

View File

@@ -6,7 +6,7 @@ import { useAppRoutes } from './useAppRoutes';
import { requestsQueryKey, useRequests } from './useRequests';
import { useActiveEnvironmentId } from './useActiveEnvironmentId';
export function useCreateRequest({ navigateAfter }: { navigateAfter: boolean }) {
export function useCreateRequest() {
const workspaceId = useActiveWorkspaceId();
const activeEnvironmentId = useActiveEnvironmentId();
const routes = useAppRoutes();
@@ -27,13 +27,11 @@ export function useCreateRequest({ navigateAfter }: { navigateAfter: boolean })
requestsQueryKey({ workspaceId: request.workspaceId }),
(requests) => [...(requests ?? []), request],
);
if (navigateAfter) {
routes.navigate('request', {
workspaceId: request.workspaceId,
requestId: request.id,
environmentId: activeEnvironmentId ?? undefined,
});
}
routes.navigate('request', {
workspaceId: request.workspaceId,
requestId: request.id,
environmentId: activeEnvironmentId ?? undefined,
});
},
});
}