From c9ee58da9b06b7ee1d4a9f67e9fe32c8ce30fe64 Mon Sep 17 00:00:00 2001 From: Sahil Sinha Date: Wed, 4 Feb 2026 18:37:11 +0530 Subject: [PATCH 1/2] Fix: handle empty collection UI and add input flow --- client/modules/IDE/components/AddToCollectionList.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/modules/IDE/components/AddToCollectionList.jsx b/client/modules/IDE/components/AddToCollectionList.jsx index 75713367ee..8fc479c2f2 100644 --- a/client/modules/IDE/components/AddToCollectionList.jsx +++ b/client/modules/IDE/components/AddToCollectionList.jsx @@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; import styled from 'styled-components'; import { Loader } from '../../App/components/Loader'; +import CollectionCreate from '../../User/components/CollectionCreate'; import { addToCollection, getCollections, @@ -63,7 +64,7 @@ const AddToCollectionList = ({ projectId }) => { if (showLoader) { return ; } else if (collections.length === 0) { - return t('AddToCollectionList.Empty'); + return ; } return ( Date: Thu, 12 Mar 2026 21:02:13 +0530 Subject: [PATCH 2/2] Fix: add inline Create Collection flow in Add to Collection modal When no collections exist, show a 'Create collection' button that reveals an inline name input. After creation, the collections list refreshes so the new collection is immediately selectable without navigating away from the current page. --- client/modules/IDE/actions/collections.js | 26 +++++++ .../IDE/components/AddToCollectionList.jsx | 74 ++++++++++++++++++- translations/locales/en-US/translations.json | 5 +- 3 files changed, 100 insertions(+), 5 deletions(-) diff --git a/client/modules/IDE/actions/collections.js b/client/modules/IDE/actions/collections.js index 68a42f500d..e7acd513b1 100644 --- a/client/modules/IDE/actions/collections.js +++ b/client/modules/IDE/actions/collections.js @@ -65,6 +65,32 @@ export function createCollection(collection) { }; } +export function createCollectionAndRefresh(collection, username) { + return (dispatch) => { + dispatch(startLoader()); + const url = '/collections'; + return apiClient + .post(url, collection) + .then((response) => { + dispatch({ type: ActionTypes.CREATE_COLLECTION }); + dispatch(stopLoader()); + + const newCollection = response.data; + dispatch(setToastText(`Created "${newCollection.name}"`)); + dispatch(showToast(TOAST_DISPLAY_TIME_MS)); + + return dispatch(getCollections(username)); + }) + .catch((error) => { + dispatch({ + type: ActionTypes.ERROR, + error: error?.response?.data + }); + dispatch(stopLoader()); + }); + }; +} + export function addToCollection(collectionId, projectId) { return (dispatch) => { dispatch(startLoader()); diff --git a/client/modules/IDE/components/AddToCollectionList.jsx b/client/modules/IDE/components/AddToCollectionList.jsx index 8fc479c2f2..86d7de0ad4 100644 --- a/client/modules/IDE/components/AddToCollectionList.jsx +++ b/client/modules/IDE/components/AddToCollectionList.jsx @@ -1,19 +1,21 @@ import PropTypes from 'prop-types'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; import styled from 'styled-components'; import { Loader } from '../../App/components/Loader'; -import CollectionCreate from '../../User/components/CollectionCreate'; +import { Button } from '../../../common/Button'; +import { generateCollectionName } from '../../../utils/generateRandomName'; import { addToCollection, + createCollectionAndRefresh, getCollections, removeFromCollection } from '../actions/collections'; import getSortedCollections from '../selectors/collections'; import QuickAddList from './QuickAddList'; -import { remSize } from '../../../theme'; +import { remSize, prop } from '../../../theme'; export const CollectionAddSketchWrapper = styled.div` width: ${remSize(600)}; @@ -28,6 +30,29 @@ export const QuickAddWrapper = styled.div` height: 100%; `; +const EmptyText = styled.p` + text-align: center; + color: ${prop('primaryTextColor')}; + margin-bottom: ${remSize(16)}; +`; + +const CreateFormWrapper = styled.form` + display: flex; + align-items: center; + gap: ${remSize(8)}; + margin-top: ${remSize(12)}; +`; + +const CreateInput = styled.input` + flex: 1; + padding: ${remSize(8)}; + border: 1px solid ${prop('Button.primary.default.border')}; + border-radius: 2px; + background-color: ${prop('inputSecondaryBackground')}; + color: ${prop('primaryTextColor')}; + font-size: ${remSize(14)}; +`; + const AddToCollectionList = ({ projectId }) => { const { t } = useTranslation(); @@ -42,6 +67,10 @@ const AddToCollectionList = ({ projectId }) => { const [hasLoadedData, setHasLoadedData] = useState(false); const showLoader = loading && !hasLoadedData; + const [showCreateForm, setShowCreateForm] = useState(false); + const generatedName = useMemo(() => generateCollectionName(), []); + const [newCollectionName, setNewCollectionName] = useState(generatedName); + useEffect(() => { dispatch(getCollections(username)).then(() => setHasLoadedData(true)); }, [dispatch, username]); @@ -54,6 +83,17 @@ const AddToCollectionList = ({ projectId }) => { dispatch(removeFromCollection(collection.id, projectId)); }; + const handleCreateCollection = (e) => { + e.preventDefault(); + if (!newCollectionName.trim()) return; + dispatch( + createCollectionAndRefresh({ name: newCollectionName.trim() }, username) + ).then(() => { + setShowCreateForm(false); + setNewCollectionName(generateCollectionName()); + }); + }; + const collectionWithSketchStatus = collections.map((collection) => ({ ...collection, url: `/${collection.owner.username}/collections/${collection.id}`, @@ -64,7 +104,33 @@ const AddToCollectionList = ({ projectId }) => { if (showLoader) { return ; } else if (collections.length === 0) { - return ; + return ( +
+ {t('AddToCollectionList.Empty')} + {showCreateForm ? ( + + setNewCollectionName(e.target.value)} + placeholder={generatedName} + aria-label={t('AddToCollectionList.CollectionNameARIA')} + /> + + + ) : ( + + )} +
+ ); } return (