move folder creation to sidebar

This commit is contained in:
2025-10-30 02:18:33 +01:00
parent e5d44c13b4
commit 9fe97f2b8a
3 changed files with 51 additions and 156 deletions
-26
View File
@@ -10,7 +10,6 @@ import {
FolderIcon,
TrashIcon,
RefreshIcon,
FolderPlusIcon,
MinusVerticalIcon,
ArrowUpIcon,
} from '../ui/icons';
@@ -199,8 +198,6 @@ const DocumentsTable = ({
currentFolderName,
onRefresh,
onShowSkeuoWorkspace = () => {},
onRequestCreateFolder,
creatingFolder = false,
subfolders,
documents,
searchResults,
@@ -512,13 +509,6 @@ const DocumentsTable = ({
<ViewGridIcon className="view-toggle__icon" size={18} />
</button>
</div>
<button
type="button"
onClick={onRequestCreateFolder}
disabled={creatingFolder}
>
{creatingFolder ? 'Creating…' : 'New folder'}
</button>
<button className="secondary" onClick={onRefresh}>
Refresh
</button>
@@ -1056,8 +1046,6 @@ export { DocumentThumbnailImage };
export const createDocumentsTableHeaderActions = ({
viewMode,
onViewModeChange,
onRequestCreateFolder,
creatingFolder,
onRefresh,
onShowSkeuoWorkspace,
}) => {
@@ -1088,16 +1076,6 @@ export const createDocumentsTableHeaderActions = ({
<span className="main-content__actions-divider" aria-hidden="true">
<MinusVerticalIcon />
</span>
<button
type="button"
className="icon-button"
onClick={onRequestCreateFolder}
disabled={creatingFolder}
aria-label={creatingFolder ? 'Creating folder…' : 'Create folder'}
title={creatingFolder ? 'Creating folder…' : 'Create folder'}
>
<FolderPlusIcon />
</button>
<button
type="button"
className="icon-button"
@@ -1127,8 +1105,6 @@ export const createDocumentsSurface = ({
searchResults,
viewMode,
onViewModeChange,
onRequestCreateFolder,
creatingFolder,
onRefresh,
onShowSkeuoWorkspace,
} = tableProps;
@@ -1141,8 +1117,6 @@ export const createDocumentsSurface = ({
const actions = createDocumentsTableHeaderActions({
viewMode,
onViewModeChange,
onRequestCreateFolder,
creatingFolder,
onRefresh,
onShowSkeuoWorkspace,
});
+27 -130
View File
@@ -440,14 +440,10 @@ const AppLayout = () => {
[reportApiError],
);
const [loading, setLoading] = useState(false);
const [isCreateFolderModalOpen, setCreateFolderModalOpen] = useState(false);
const [isTagsModalOpen, setTagsModalOpen] = useState(false);
const [isCorrespondentsModalOpen, setCorrespondentsModalOpen] = useState(false);
const [isSettingsModalOpen, setSettingsModalOpen] = useState(false);
const [newFolderName, setNewFolderName] = useState('');
const [createFolderError, setCreateFolderError] = useState('');
const [creatingFolder, setCreatingFolder] = useState(false);
const createFolderInputRef = useRef(null);
const [folderNodes, setFolderNodes] = useState(() => {
const rootNode = createRootNode();
return new Map([[rootNode.id, rootNode]]);
@@ -3852,7 +3848,7 @@ const AppLayout = () => {
);
const handleFolderCreate = useCallback(
async (name, onSuccess) => {
async (name) => {
if (!token) {
setStatusMessage('Log in to create folders.', 'error');
return false;
@@ -3869,7 +3865,6 @@ const AppLayout = () => {
let succeeded = false;
try {
const { data } = await api.post('/folders', payload);
onSuccess();
setStatusMessage('Folder created.', 'success');
setFolderNodes((prev) => {
const next = new Map(prev);
@@ -3895,6 +3890,7 @@ const AppLayout = () => {
return next;
});
await ensureFolderData(selectedFolder, { force: true, prefetchDepth: 1 });
await selectFolder(data.folder.id, { immediate: true });
succeeded = true;
} catch (error) {
const message = error.response?.data?.error || 'Failed to create folder.';
@@ -3905,23 +3901,32 @@ const AppLayout = () => {
}
return succeeded;
},
[token, selectedFolder, ensureFolderData, notifyApiError, setStatusMessage],
[token, selectedFolder, ensureFolderData, notifyApiError, setStatusMessage, selectFolder],
);
const openCreateFolderModal = useCallback(() => {
setNewFolderName('');
setCreateFolderError('');
setCreateFolderModalOpen(true);
}, []);
const closeCreateFolderModal = useCallback(() => {
const handlePromptCreateFolder = useCallback(async () => {
if (creatingFolder) {
return;
}
setCreateFolderModalOpen(false);
setNewFolderName('');
setCreateFolderError('');
}, [creatingFolder]);
const input = window.prompt('New folder name');
if (!input) {
return;
}
const trimmed = input.trim();
if (!trimmed) {
setStatusMessage('Folder name cannot be empty.', 'error');
return;
}
setCreatingFolder(true);
try {
const success = await handleFolderCreate(trimmed);
if (!success) {
setStatusMessage('Unable to create folder. Check the status message for details.', 'error');
}
} finally {
setCreatingFolder(false);
}
}, [creatingFolder, handleFolderCreate, setStatusMessage]);
const openTagsModal = useCallback(() => {
setCorrespondentsModalOpen(false);
@@ -3949,66 +3954,6 @@ const AppLayout = () => {
setSettingsModalOpen(false);
}, []);
const handleCreateFolderSubmit = useCallback(
async (event) => {
event.preventDefault();
const trimmed = newFolderName.trim();
if (!trimmed) {
setCreateFolderError('Folder name cannot be empty.');
return;
}
if (!token) {
setCreateFolderError('Log in to create folders.');
return;
}
setCreatingFolder(true);
setCreateFolderError('');
try {
const success = await handleFolderCreate(trimmed, () => {
setCreateFolderModalOpen(false);
setNewFolderName('');
setCreateFolderError('');
});
if (!success) {
setCreateFolderError('Unable to create folder. Check the status message for details.');
}
} finally {
setCreatingFolder(false);
}
},
[newFolderName, handleFolderCreate, token],
);
useEffect(() => {
if (!isCreateFolderModalOpen) {
return;
}
const node = createFolderInputRef.current;
if (node) {
node.focus();
node.select();
}
}, [isCreateFolderModalOpen]);
useEffect(() => {
if (!isCreateFolderModalOpen) {
return;
}
const handleKeyDown = (event) => {
if (event.key === 'Escape') {
event.preventDefault();
closeCreateFolderModal();
}
};
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [isCreateFolderModalOpen, closeCreateFolderModal]);
useEffect(() => {
setTagsModalOpen(false);
setCorrespondentsModalOpen(false);
@@ -4985,8 +4930,6 @@ const AppLayout = () => {
breadcrumbs,
onRefresh: refreshCurrentFolder,
onShowSkeuoWorkspace: showSkeuoWorkspace,
onRequestCreateFolder: openCreateFolderModal,
creatingFolder,
subfolders: currentSubfolders,
documents,
searchResults,
@@ -5059,8 +5002,6 @@ const AppLayout = () => {
handleFolderRename,
handleFolderRowClick,
isFilterActive,
creatingFolder,
openCreateFolderModal,
openDocumentPreview,
refreshCurrentFolder,
searchLoading,
@@ -5092,6 +5033,8 @@ const AppLayout = () => {
onFolderDragStart: handleFolderDragStart,
onFolderDragEnd: handleFolderDragEnd,
draggedFolderId,
onCreateFolder: handlePromptCreateFolder,
creatingFolder,
tags,
activeTagIds: activeTagFilters,
onToggleTagFilter: toggleTagFilter,
@@ -5148,6 +5091,8 @@ const AppLayout = () => {
toggleTagFilter,
handleTagCreate,
handleCorrespondentCreate,
handlePromptCreateFolder,
creatingFolder,
],
);
@@ -5360,54 +5305,6 @@ const AppLayout = () => {
folderName={dropOverlayState.folderName}
/>
<Outlet />
{isCreateFolderModalOpen && (
<div
className="modal-backdrop"
role="presentation"
onClick={closeCreateFolderModal}
>
<div
className="modal"
role="dialog"
aria-modal="true"
aria-labelledby="create-folder-title"
onClick={(event) => event.stopPropagation()}
>
<h3 id="create-folder-title">Create folder</h3>
<form className="modal__form" onSubmit={handleCreateFolderSubmit}>
<label htmlFor="new-folder-name">Folder name</label>
<input
id="new-folder-name"
ref={createFolderInputRef}
value={newFolderName}
onChange={(event) => {
setNewFolderName(event.target.value);
if (createFolderError) {
setCreateFolderError('');
}
}}
placeholder="Enter folder name"
disabled={creatingFolder}
autoComplete="off"
/>
{createFolderError && <p className="modal__error">{createFolderError}</p>}
<div className="modal__actions">
<button
type="button"
className="secondary"
onClick={closeCreateFolderModal}
disabled={creatingFolder}
>
Cancel
</button>
<button type="submit" disabled={creatingFolder}>
{creatingFolder ? 'Creating…' : 'Create'}
</button>
</div>
</form>
</div>
</div>
)}
{isTagsModalOpen && (
<div
className="modal-backdrop"
+24
View File
@@ -10,6 +10,7 @@ import {
SettingsIcon,
CheckIcon,
PlusIcon,
FolderPlusIcon,
} from '../ui/icons';
import { getTagColorStyle } from '../utils/colors';
@@ -139,6 +140,8 @@ const Sidebar = ({
onFolderDragStart,
onFolderDragEnd,
draggedFolderId,
onCreateFolder,
creatingFolder = false,
tags = [],
activeTagIds = [],
onToggleTagFilter,
@@ -209,6 +212,14 @@ const Sidebar = ({
console.error('[sidebar] failed to create correspondent', error);
}
}, [onCreateCorrespondent]);
const handleCreateFolder = useCallback(() => {
if (creatingFolder) {
return;
}
onCreateFolder?.();
}, [creatingFolder, onCreateFolder]);
const handleSearchInputChange = useCallback(
(event) => {
onSearchChange?.(event.target.value);
@@ -442,6 +453,19 @@ const Sidebar = ({
<div className="sidebar-section sidebar-section--folders">
<div className="sidebar-section__header">
<h3>Folders</h3>
{onCreateFolder ? (
<div className="sidebar-section__actions">
<button
type="button"
className="icon-button"
onClick={handleCreateFolder}
aria-label="Create folder"
disabled={creatingFolder}
>
<FolderPlusIcon size={16} />
</button>
</div>
) : null}
</div>
<ul className="folder-tree">
{rootNode && renderNodes([rootNode.id], 0)}