collection runner tag updates

This commit is contained in:
lohit-bruno
2025-07-15 14:33:18 +05:30
parent 3803576aa4
commit 0e28c97f8f
32 changed files with 920 additions and 342 deletions

View File

@@ -19,7 +19,7 @@ import StyledWrapper from './StyledWrapper';
import Documentation from 'components/Documentation/index';
import GraphQLSchemaActions from '../GraphQLSchemaActions/index';
import HeightBoundContainer from 'ui/HeightBoundContainer';
import Tags from 'components/RequestPane/Tags/index';
import Settings from 'components/RequestPane/Settings';
const GraphQLRequestPane = ({ item, collection, onSchemaLoad, toggleDocs, handleGqlClickReference }) => {
const dispatch = useDispatch();
@@ -102,8 +102,8 @@ const GraphQLRequestPane = ({ item, collection, onSchemaLoad, toggleDocs, handle
case 'docs': {
return <Documentation item={item} collection={collection} />;
}
case 'tags': {
return <Tags item={item} collection={collection} />;
case 'settings': {
return <Settings item={item} collection={collection} />;
}
default: {
return <div className="mt-4">404 | Not found</div>;
@@ -156,8 +156,8 @@ const GraphQLRequestPane = ({ item, collection, onSchemaLoad, toggleDocs, handle
<div className={getTabClassname('docs')} role="tab" onClick={() => selectTab('docs')}>
Docs
</div>
<div className={getTabClassname('tags')} role="tab" onClick={() => selectTab('tags')}>
Tags
<div className={getTabClassname('settings')} role="tab" onClick={() => selectTab('settings')}>
Settings
</div>
<GraphQLSchemaActions item={item} collection={collection} onSchemaLoad={setSchema} toggleDocs={toggleDocs} />
</div>

View File

@@ -18,7 +18,6 @@ import HeightBoundContainer from 'ui/HeightBoundContainer';
import { useEffect } from 'react';
import StatusDot from 'components/StatusDot';
import Settings from 'components/RequestPane/Settings';
import Tags from 'components/RequestPane/Tags/index';
const HttpRequestPane = ({ item, collection }) => {
const dispatch = useDispatch();
@@ -66,9 +65,6 @@ const HttpRequestPane = ({ item, collection }) => {
case 'settings': {
return <Settings item={item} collection={collection} />;
}
case 'tags': {
return <Tags item={item} collection={collection} />;
}
default: {
return <div className="mt-4">404 | Not found</div>;
}
@@ -105,6 +101,7 @@ const HttpRequestPane = ({ item, collection }) => {
const requestVars = getPropertyFromDraftOrRequest('request.vars.req');
const responseVars = getPropertyFromDraftOrRequest('request.vars.res');
const auth = getPropertyFromDraftOrRequest('request.auth');
const tags = getPropertyFromDraftOrRequest('tags');
const activeParamsLength = params.filter((param) => param.enabled).length;
const activeHeadersLength = headers.filter((header) => header.enabled).length;
@@ -168,9 +165,7 @@ const HttpRequestPane = ({ item, collection }) => {
</div>
<div className={getTabClassname('settings')} role="tab" onClick={() => selectTab('settings')}>
Settings
</div>
<div className={getTabClassname('tags')} role="tab" onClick={() => selectTab('tags')}>
Tags
{tags && tags.length > 0 && <StatusDot />}
</div>
{focusedTab.requestPaneTab === 'body' ? (
<div className="flex flex-grow justify-end items-center">

View File

@@ -0,0 +1,63 @@
import React, { useCallback, useEffect } from 'react';
import get from 'lodash/get';
import { useDispatch } from 'react-redux';
import { addRequestTag, deleteRequestTag, updateCollectionTagsList } from 'providers/ReduxStore/slices/collections';
import TagList from 'components/TagList/index';
import { saveRequest } from 'providers/ReduxStore/slices/collections/actions';
const Tags = ({ item, collection }) => {
const dispatch = useDispatch();
// all tags in the collection
const collectionTags = collection.allTags || [];
// tags for the current request
const tags = item.draft ? get(item, 'draft.tags', []) : get(item, 'tags', []);
// Filter out tags that are already associated with the current request
const collectionTagsWithoutCurrentRequestTags = collectionTags?.filter(tag => !tags.includes(tag)) || [];
const handleAdd = useCallback((tag) => {
const trimmedTag = tag.trim();
if (trimmedTag && !tags.includes(trimmedTag)) {
dispatch(
addRequestTag({
tag: trimmedTag,
itemUid: item.uid,
collectionUid: collection.uid
})
);
}
}, [dispatch, tags, item.uid, collection.uid]);
const handleRemove = useCallback((tag) => {
dispatch(
deleteRequestTag({
tag,
itemUid: item.uid,
collectionUid: collection.uid
})
);
}, [dispatch, item.uid, collection.uid]);
const handleRequestSave = () => {
dispatch(saveRequest(item.uid, collection.uid));
}
useEffect(() => {
dispatch(updateCollectionTagsList({ collectionUid: collection.uid }));
}, [collection.uid, dispatch]);
return (
<div className="flex flex-col">
<TagList
tagsHintList={collectionTagsWithoutCurrentRequestTags}
handleAddTag={handleAdd}
handleRemoveTag={handleRemove}
tags={tags}
onSave={handleRequestSave}
/>
</div>
);
};
export default Tags;

View File

@@ -1,8 +1,10 @@
import React, { useState, useCallback } from 'react';
import React, { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import get from 'lodash/get';
import { IconTag } from '@tabler/icons';
import ToggleSelector from 'components/RequestPane/Settings/ToggleSelector';
import { updateItemSettings } from 'providers/ReduxStore/slices/collections';
import Tags from './Tags/index';
const Settings = ({ item, collection }) => {
const dispatch = useDispatch();
@@ -22,7 +24,16 @@ const Settings = ({ item, collection }) => {
}, [encodeUrl, dispatch, collection.uid, item.uid]);
return (
<div className="h-full flex flex-col gap-2">
<div className="w-full h-full flex flex-col gap-10">
<div className='flex flex-col gap-2 max-w-[400px]'>
<h3 className="text-xs font-medium text-gray-900 dark:text-gray-100 flex items-center gap-1">
<IconTag size={16} />
Tags
</h3>
<div label="Tags">
<Tags item={item} collection={collection} />
</div>
</div>
<div className='flex flex-col gap-4'>
<ToggleSelector
checked={encodeUrl}

View File

@@ -1,25 +0,0 @@
import styled from 'styled-components';
const Wrapper = styled.div`
input[type='text'] {
border: solid 1px transparent;
outline: none !important;
background-color: inherit;
&:focus {
outline: none !important;
border: solid 1px transparent;
}
}
li {
display: flex;
align-items: center;
border: 1px solid ${(props) => props.theme.text};
border-radius: 5px;
padding-inline: 5px;
background: ${(props) => props.theme.sidebar.bg};
}
`;
export default Wrapper;

View File

@@ -1,69 +0,0 @@
import { IconX } from '@tabler/icons';
import { useState } from 'react';
import toast from 'react-hot-toast';
import StyledWrapper from './StyledWrapper';
const TagList = ({ tags, onTagRemove, onTagAdd }) => {
const tagNameRegex = /^[\w-]+$/;
const [isEditing, setIsEditing] = useState(false);
const [text, setText] = useState('');
const handleChange = (e) => {
setText(e.target.value);
};
const handleKeyDown = (e) => {
if (e.code == 'Escape') {
setText('');
setIsEditing(false);
return;
}
if (e.code !== 'Enter' && e.code !== 'Space') {
return;
}
if (!tagNameRegex.test(text)) {
toast.error('Tags must only contain alpha-numeric characters, "-", "_"');
return;
}
if (tags.includes(text)) {
toast.error(`Tag "${text}" already exists`);
return;
}
onTagAdd(text);
setText('');
setIsEditing(false);
};
return (
<StyledWrapper className="flex flex-wrap gap-2 mt-1">
<ul className="flex flex-wrap gap-1">
{tags && tags.length
? tags.map((_tag) => (
<li key={_tag}>
<span>{_tag}</span>
<button tabIndex={-1} onClick={() => onTagRemove(_tag)}>
<IconX strokeWidth={1.5} size={20} />
</button>
</li>
))
: null}
</ul>
{isEditing ? (
<input
type="text"
placeholder="Space or Enter to add tag"
value={text}
onChange={handleChange}
onKeyDown={handleKeyDown}
autoFocus
/>
) : (
<button className="text-link select-none" onClick={() => setIsEditing(true)}>
+ Add
</button>
)}
</StyledWrapper>
);
};
export default TagList;

View File

@@ -1,43 +0,0 @@
import 'github-markdown-css/github-markdown.css';
import get from 'lodash/get';
import { addRequestTag, deleteRequestTag } from 'providers/ReduxStore/slices/collections';
import { useDispatch } from 'react-redux';
import TagList from './TagList/TagList';
const Tags = ({ item, collection }) => {
const tags = item.draft ? get(item, 'draft.request.tags') : get(item, 'request.tags');
const dispatch = useDispatch();
const handleAdd = (_tag) => {
dispatch(
addRequestTag({
tag: _tag,
itemUid: item.uid,
collectionUid: collection.uid
})
);
};
const handleRemove = (_tag) => {
dispatch(
deleteRequestTag({
tag: _tag,
itemUid: item.uid,
collectionUid: collection.uid
})
);
};
if (!item) {
return null;
}
return (
<div>
<TagList tags={tags} onTagRemove={handleRemove} onTagAdd={handleAdd} />
</div>
);
};
export default Tags;