move folder creation to sidebar
This commit is contained in:
@@ -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
@@ -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"
|
||||
|
||||
@@ -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)}
|
||||
|
||||
Reference in New Issue
Block a user