mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-07-07 05:15:20 +02:00
feat: add breadcrumbs to folder setting (#296)
This commit is contained in:
@@ -1,21 +1,14 @@
|
|||||||
import { getModel } from '@yaakapp-internal/models';
|
import { getModel } from '@yaakapp-internal/models';
|
||||||
import { Icon } from '../components/core/Icon';
|
|
||||||
import { HStack } from '../components/core/Stacks';
|
|
||||||
import type { FolderSettingsTab } from '../components/FolderSettingsDialog';
|
import type { FolderSettingsTab } from '../components/FolderSettingsDialog';
|
||||||
import { FolderSettingsDialog } from '../components/FolderSettingsDialog';
|
import { FolderSettingsDialog } from '../components/FolderSettingsDialog';
|
||||||
import { showDialog } from '../lib/dialog';
|
import { showDialog } from '../lib/dialog';
|
||||||
import { resolvedModelName } from '../lib/resolvedModelName';
|
|
||||||
|
|
||||||
export function openFolderSettings(folderId: string, tab?: FolderSettingsTab) {
|
export function openFolderSettings(folderId: string, tab?: FolderSettingsTab) {
|
||||||
const folder = getModel('folder', folderId);
|
const folder = getModel('folder', folderId);
|
||||||
|
if (folder == null) return;
|
||||||
showDialog({
|
showDialog({
|
||||||
id: 'folder-settings',
|
id: 'folder-settings',
|
||||||
title: (
|
title: null,
|
||||||
<HStack space={2} alignItems="center">
|
|
||||||
<Icon icon="folder_cog" size="xl" color="secondary" />
|
|
||||||
{resolvedModelName(folder)}
|
|
||||||
</HStack>
|
|
||||||
),
|
|
||||||
size: 'lg',
|
size: 'lg',
|
||||||
className: 'h-[50rem]',
|
className: 'h-[50rem]',
|
||||||
noPadding: true,
|
noPadding: true,
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
import { createWorkspaceModel, foldersAtom, patchModel } from '@yaakapp-internal/models';
|
import {
|
||||||
|
createWorkspaceModel,
|
||||||
|
foldersAtom,
|
||||||
|
patchModel,
|
||||||
|
} from '@yaakapp-internal/models';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import { useMemo } from 'react';
|
import { Fragment, useMemo } from 'react';
|
||||||
import { useAuthTab } from '../hooks/useAuthTab';
|
import { useAuthTab } from '../hooks/useAuthTab';
|
||||||
import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown';
|
import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown';
|
||||||
import { useHeadersTab } from '../hooks/useHeadersTab';
|
import { useHeadersTab } from '../hooks/useHeadersTab';
|
||||||
import { useInheritedHeaders } from '../hooks/useInheritedHeaders';
|
import { useInheritedHeaders } from '../hooks/useInheritedHeaders';
|
||||||
|
import { useModelAncestors } from '../hooks/useModelAncestors';
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
import { CountBadge } from './core/CountBadge';
|
import { CountBadge } from './core/CountBadge';
|
||||||
|
import { Icon } from './core/Icon';
|
||||||
import { Input } from './core/Input';
|
import { Input } from './core/Input';
|
||||||
import { Link } from './core/Link';
|
import { Link } from './core/Link';
|
||||||
import { VStack } from './core/Stacks';
|
import { VStack } from './core/Stacks';
|
||||||
@@ -37,6 +43,8 @@ export type FolderSettingsTab =
|
|||||||
export function FolderSettingsDialog({ folderId, tab }: Props) {
|
export function FolderSettingsDialog({ folderId, tab }: Props) {
|
||||||
const folders = useAtomValue(foldersAtom);
|
const folders = useAtomValue(foldersAtom);
|
||||||
const folder = folders.find((f) => f.id === folderId) ?? null;
|
const folder = folders.find((f) => f.id === folderId) ?? null;
|
||||||
|
const ancestors = useModelAncestors(folder);
|
||||||
|
const breadcrumbs = useMemo(() => ancestors.toReversed(), [ancestors]);
|
||||||
const authTab = useAuthTab(TAB_AUTH, folder);
|
const authTab = useAuthTab(TAB_AUTH, folder);
|
||||||
const headersTab = useHeadersTab(TAB_HEADERS, folder);
|
const headersTab = useHeadersTab(TAB_HEADERS, folder);
|
||||||
const inheritedHeaders = useInheritedHeaders(folder);
|
const inheritedHeaders = useInheritedHeaders(folder);
|
||||||
@@ -67,76 +75,107 @@ export function FolderSettingsDialog({ folderId, tab }: Props) {
|
|||||||
if (folder == null) return null;
|
if (folder == null) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<div className="h-full flex flex-col">
|
||||||
defaultValue={tab ?? TAB_GENERAL}
|
<div className="flex items-center gap-3 px-6 pr-10 mt-4 mb-2 min-w-0 text-xl">
|
||||||
label="Folder Settings"
|
<Icon icon="folder_cog" size="lg" color="secondary" className="flex-shrink-0" />
|
||||||
className="pt-2 pb-2 pl-3 pr-1"
|
<div className="flex items-center gap-1.5 font-semibold text-text min-w-0 overflow-hidden flex-1">
|
||||||
layout="horizontal"
|
{breadcrumbs.map((item, index) => (
|
||||||
addBorders
|
<Fragment key={item.id}>
|
||||||
tabs={tabs}
|
{index > 0 && (
|
||||||
>
|
<Icon
|
||||||
<TabContent value={TAB_AUTH} className="overflow-y-auto h-full px-4">
|
icon="chevron_right"
|
||||||
<HttpAuthenticationEditor model={folder} />
|
size="lg"
|
||||||
</TabContent>
|
className="opacity-50 flex-shrink-0"
|
||||||
<TabContent value={TAB_GENERAL} className="overflow-y-auto h-full px-4">
|
/>
|
||||||
<VStack space={3} className="pb-3 h-full">
|
)}
|
||||||
<Input
|
<span className="text-text-subtle truncate min-w-0" title={item.name}>
|
||||||
label="Folder Name"
|
{item.name}
|
||||||
defaultValue={folder.name}
|
</span>
|
||||||
onChange={(name) => patchModel(folder, { name })}
|
</Fragment>
|
||||||
stateKey={`name.${folder.id}`}
|
))}
|
||||||
|
{breadcrumbs.length > 0 && (
|
||||||
|
<Icon icon="chevron_right" size="lg" className="opacity-50 flex-shrink-0" />
|
||||||
|
)}
|
||||||
|
<span
|
||||||
|
className="whitespace-nowrap"
|
||||||
|
title={folder.name}
|
||||||
|
>
|
||||||
|
{folder.name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
defaultValue={tab ?? TAB_GENERAL}
|
||||||
|
label="Folder Settings"
|
||||||
|
className="pt-2 pb-2 pl-3 pr-1 flex-1"
|
||||||
|
layout="horizontal"
|
||||||
|
addBorders
|
||||||
|
tabs={tabs}
|
||||||
|
>
|
||||||
|
<TabContent value={TAB_AUTH} className="overflow-y-auto h-full px-4">
|
||||||
|
<HttpAuthenticationEditor model={folder} />
|
||||||
|
</TabContent>
|
||||||
|
<TabContent value={TAB_GENERAL} className="overflow-y-auto h-full px-4">
|
||||||
|
<VStack space={3} className="pb-3 h-full">
|
||||||
|
<Input
|
||||||
|
label="Folder Name"
|
||||||
|
defaultValue={folder.name}
|
||||||
|
onChange={(name) => patchModel(folder, { name })}
|
||||||
|
stateKey={`name.${folder.id}`}
|
||||||
|
/>
|
||||||
|
<MarkdownEditor
|
||||||
|
name="folder-description"
|
||||||
|
placeholder="Folder description"
|
||||||
|
className="border border-border px-2"
|
||||||
|
defaultValue={folder.description}
|
||||||
|
stateKey={`description.${folder.id}`}
|
||||||
|
onChange={(description) => patchModel(folder, { description })}
|
||||||
|
/>
|
||||||
|
</VStack>
|
||||||
|
</TabContent>
|
||||||
|
<TabContent value={TAB_HEADERS} className="overflow-y-auto h-full px-4">
|
||||||
|
<HeadersEditor
|
||||||
|
inheritedHeaders={inheritedHeaders}
|
||||||
|
forceUpdateKey={folder.id}
|
||||||
|
headers={folder.headers}
|
||||||
|
onChange={(headers) => patchModel(folder, { headers })}
|
||||||
|
stateKey={`headers.${folder.id}`}
|
||||||
/>
|
/>
|
||||||
<MarkdownEditor
|
</TabContent>
|
||||||
name="folder-description"
|
<TabContent value={TAB_VARIABLES} className="overflow-y-auto h-full px-4">
|
||||||
placeholder="Folder description"
|
{folderEnvironment == null ? (
|
||||||
className="border border-border px-2"
|
<EmptyStateText>
|
||||||
defaultValue={folder.description}
|
<VStack alignItems="center" space={1.5}>
|
||||||
stateKey={`description.${folder.id}`}
|
<p>
|
||||||
onChange={(description) => patchModel(folder, { description })}
|
Override{' '}
|
||||||
/>
|
<Link href="https://yaak.app/docs/using-yaak/environments-and-variables">
|
||||||
</VStack>
|
Variables
|
||||||
</TabContent>
|
</Link>{' '}
|
||||||
<TabContent value={TAB_HEADERS} className="overflow-y-auto h-full px-4">
|
for requests within this folder.
|
||||||
<HeadersEditor
|
</p>
|
||||||
inheritedHeaders={inheritedHeaders}
|
<Button
|
||||||
forceUpdateKey={folder.id}
|
variant="border"
|
||||||
headers={folder.headers}
|
size="sm"
|
||||||
onChange={(headers) => patchModel(folder, { headers })}
|
onClick={async () => {
|
||||||
stateKey={`headers.${folder.id}`}
|
await createWorkspaceModel({
|
||||||
/>
|
workspaceId: folder.workspaceId,
|
||||||
</TabContent>
|
parentModel: 'folder',
|
||||||
<TabContent value={TAB_VARIABLES} className="overflow-y-auto h-full px-4">
|
parentId: folder.id,
|
||||||
{folderEnvironment == null ? (
|
model: 'environment',
|
||||||
<EmptyStateText>
|
name: 'Folder Environment',
|
||||||
<VStack alignItems="center" space={1.5}>
|
});
|
||||||
<p>
|
}}
|
||||||
Override{' '}
|
>
|
||||||
<Link href="https://yaak.app/docs/using-yaak/environments-and-variables">
|
Create Folder Environment
|
||||||
Variables
|
</Button>
|
||||||
</Link>{' '}
|
</VStack>
|
||||||
for requests within this folder.
|
</EmptyStateText>
|
||||||
</p>
|
) : (
|
||||||
<Button
|
<EnvironmentEditor hideName environment={folderEnvironment} />
|
||||||
variant="border"
|
)}
|
||||||
size="sm"
|
</TabContent>
|
||||||
onClick={async () => {
|
</Tabs>
|
||||||
await createWorkspaceModel({
|
</div>
|
||||||
workspaceId: folder.workspaceId,
|
|
||||||
parentModel: 'folder',
|
|
||||||
parentId: folder.id,
|
|
||||||
model: 'environment',
|
|
||||||
name: 'Folder Environment',
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Create Folder Environment
|
|
||||||
</Button>
|
|
||||||
</VStack>
|
|
||||||
</EmptyStateText>
|
|
||||||
) : (
|
|
||||||
<EnvironmentEditor hideName environment={folderEnvironment} />
|
|
||||||
)}
|
|
||||||
</TabContent>
|
|
||||||
</Tabs>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ export function Workspace() {
|
|||||||
open={!floatingSidebarHidden}
|
open={!floatingSidebarHidden}
|
||||||
portalName="sidebar"
|
portalName="sidebar"
|
||||||
onClose={() => setFloatingSidebarHidden(true)}
|
onClose={() => setFloatingSidebarHidden(true)}
|
||||||
|
zIndex={20}
|
||||||
>
|
>
|
||||||
<m.div
|
<m.div
|
||||||
initial={{ opacity: 0, x: -20 }}
|
initial={{ opacity: 0, x: -20 }}
|
||||||
|
|||||||
Reference in New Issue
Block a user