Skip to content
18 changes: 8 additions & 10 deletions apps/frontend/app/components/Tag.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { XMarkIcon } from '@heroicons/react/24/solid';

export function Tag({deletable, text}){
export function Tag({deletable, text, onDelete}){
return (
deletable ?
(<div className="cursor-pointer gap-1 flex rounded-md bg-blue-400/10 px-2 py-1 text-xs font-medium text-blue-400 inset-ring inset-ring-blue-400/30">
<span className="h-full hover:underline flex-1">{text}</span>
<div>
<XMarkIcon className="flex-none text-blue-400 cursor-pointer w-4 h-4 grid-cols-1" />
</div>
</div>) :
(<div>
<span className="inline-flex items-center rounded-md bg-blue-400/10 px-2 py-1 text-xs font-medium text-blue-400 inset-ring inset-ring-blue-400/30">{text}</span>
(<div className="cursor-pointer gap-1 flex rounded-full border border-blue-200 bg-blue-400/10 px-2 py-1 text-xs font-medium text-blue-400 inset-ring inset-ring-blue-400/30">
<span className="h-full flex-1">{text}</span>
{deletable ? (
<div onClick={onDelete}>
<XMarkIcon className="flex-none text-blue-400 cursor-pointer w-4 h-4 grid-cols-1" />
</div>
) : null}
</div>)
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const InformationStep = ({ form, validationErrors, setValidationErrors, .
type='text'
value={individualTag}
maxLength={40}
placeholder='Enter tag name and select "Add" or press "Return" to submit'
placeholder='Press "Return" or click "Add tag" to confirm tag'
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
Expand All @@ -86,17 +86,17 @@ export const InformationStep = ({ form, validationErrors, setValidationErrors, .
/>
</div>
<div className=''>
<button className='rounded-md w-full border border-transparent bg-blue-600 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2'
<button className='w-full border border-dashed border-blue-500 rounded-md px-3 py-2 text-sm font-medium text-blue-500 hover:text-blue-700 hover:border-blue-700'
onClick={addTagValue}>
Add
+ Add tag
</button>
</div>
</div>
<div className='w-full flex flex-wrap gap-1'>
{Array.isArray(form.values.tags) &&
form.values.tags.map((title) =>(
<div key={title} onClick={() => deleteTag(title)}>
<Tag deletable={true} text={title}/>
<div key={title}>
<Tag deletable={true} text={title} onDelete={() => deleteTag(title)}/>
</div>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,19 +87,8 @@ export const ExperimentListing = ({ projectData: projectData, onCopyExperiment,
setIsEditing(false);
};

const handleSaveTags = () => {
updateExperimentTagsById(project.expId, projectTags).catch((reason) => {
console.warn(`Failed to update experiment name, reason: ${reason}`);
});
// Exit the editing mode
setIndividualTag("");
setIsEditingTags(false);
};

const handleCancelTags = () => {
setProjectTags(originalProjectTags);
setIndividualTag("");
setEditingTagsCanceled(true);
setIsEditingTags(false);
}

Expand All @@ -117,7 +106,11 @@ export const ExperimentListing = ({ projectData: projectData, onCopyExperiment,
};

const deleteTag = (title) => {
setProjectTags(projectTags.filter(tagName => tagName !== title));
const newTags = projectTags.filter(tagName => tagName !== title);
setProjectTags(newTags);
updateExperimentTagsById(project.expId, newTags).catch((reason) => {
console.warn(`Failed to delete tag, reason: ${reason}`);
});
}

useEffect(() => {
Expand Down Expand Up @@ -146,6 +139,16 @@ export const ExperimentListing = ({ projectData: projectData, onCopyExperiment,
}
};

const handleKeyUpForTags = (e) => {
if (e.key === 'Enter') {
const newTags = [...projectTags, individualTag.trim()];
addTagValue(newTags);
} else if (e.key === 'Escape') {
handleCancel();
handleCancelTags();
}
};

// Function to open the delete modal
const openDeleteModal = () => {
setDeleteModalOpen(true);
Expand Down Expand Up @@ -186,7 +189,7 @@ export const ExperimentListing = ({ projectData: projectData, onCopyExperiment,
// : `${averageTimePerRun.toFixed(2)} minutes`
// : null;

const addTagValue = () => {
const addTagValue = (newTags: string[]) => {
if(projectTags && (projectTags.includes(individualTag.trim()))){
toast.error("Experiment tags cannot be redundant.", {duration: 1500});
return;
Expand All @@ -197,8 +200,11 @@ export const ExperimentListing = ({ projectData: projectData, onCopyExperiment,
toast.error("Experiment tag cannot be blank.", {duration: 1500});
return;
} else {
setProjectTags([...projectTags, individualTag]);
setIndividualTag("");
setProjectTags(newTags);
updateExperimentTagsById(project.expId, newTags).catch((reason) => {
console.warn(`Failed to update experiment name, reason: ${reason}`);
});
setIndividualTag("");
}
}

Expand Down Expand Up @@ -482,59 +488,47 @@ export const ExperimentListing = ({ projectData: projectData, onCopyExperiment,
}

{isEditingTags ? (
<div className="flex items-center flex-wrap gap-1 justify-left">
{ <div className="flex items-center gap-2">
<div className="flex items-center gap-1">
<div className="flex flex-col gap-1 justify-left">
{<div className="flex items-center flex-wrap gap-1">
<div className="inline-flex items-center">
<input
type="text"
value={individualTag}
maxLength={40}
placeholder='Select "Add" for New Tag'
placeholder='New tag'
onChange={(e) => setIndividualTag(e.target.value)}
className="py-2 px-3 text-sm text-left font-medium"
onKeyUp={handleKeyUpForTags}
className="outline-none bg-transparent text-xs border border-blue-500 border-solid rounded-full px-2 py-1 font-mono"
/>
<button
onClick={addTagValue}
className="rounded-md border border-transparent bg-blue-600 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-blue-700"
>
Add
</button>
</div>
<div className="flex items-center gap-0">
<CheckIcon
className="w-10 h-5 text-green-500 cursor-pointer"
onClick={handleSaveTags}
/>
<XMarkIcon
className="w-5 h-5 text-red-500 cursor-pointer"
onClick={handleCancelTags}
/>
</div>
</div>
}
<div className="w-full flex flex-wrap gap-2">
{projectTags &&
{projectTags &&
projectTags.map((title) =>(
<div key={title} onClick={() => deleteTag(title)}>
<Tag deletable={true} text={title}/>
<div key={title}>
<Tag deletable={true} text={title} onDelete={() => deleteTag(title)}/>
</div>
))}
</div>}
<div className="flex items-center gap-2">
<p className="text-gray-400 font-mono mt-2" style={{ fontSize: 10 }}>
Press <kbd className="bg-gray-100 border border-gray-300 rounded px-0.5 py-0.5 text-xs">Return</kbd> to confirm,{" "}
<kbd className="bg-gray-100 border border-gray-300 rounded px-0.5 py-0.5 text-xs">Esc</kbd> to cancel.
</p>
</div>
</div>) :
(
<div className="flex items-center flex-wrap gap-1 justify-left">
{projectTags && projectTags.length > 0 ?
projectTags.map((title) =>(
<Tag key={title} deletable={false} text={title} />
)) :
<p className="text-sm font-mono text-gray-500">Click to Add Tags</p>}
{project.creator == session?.user?.id! ? <MdEdit
className="icon edit-icon"
onClick={handleEditTags}
style={{flexShrink: 0}}
/> : <></>}
{projectTags.length < TAG_MAX_NUMBER && project.creator == session?.user?.id! &&
<p className="inline-flex items-center gap-1 border border-dashed border-blue-300 rounded-full px-3 py-1 text-xs text-blue-400 hover:border-blue-400 font-medium"
onClick={handleEditTags}>
+ Add tag
</p>}
{projectTags.map((title) =>(
<div key={title}>
<Tag deletable={true} text={title} onDelete={() => deleteTag(title)}/>
</div>
))}
</div>
) }
)}

<div className="text-sm font-mono text-gray-500 sm:hidden text-left">
{getStatusText(project, experimentStates[project.expId])}
Expand Down
2 changes: 1 addition & 1 deletion apps/frontend/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1143,7 +1143,7 @@ const ExperimentList = ({ experiments, onCopyExperiment, onDeleteExperiment, sea
handleMultipleFilterTag(title, newValue);
}}
/>
<Tag text={title} deletable={false} />
<Tag deletable={false} text={title} onDelete={() => {}}/>
</a>
)}
</MenuItem>
Expand Down
Loading