frontend cleanup

This commit is contained in:
2025-10-29 00:02:44 +01:00
parent 8554fbd6e5
commit b4282c2b4b
4 changed files with 164 additions and 88 deletions
+91 -83
View File
@@ -29,6 +29,7 @@ import TagsPanel from './tags/TagsPanel';
import CorrespondentsPanel from './correspondents/CorrespondentsPanel';
import TagManager from './tag_manager';
import Sidebar from './sidebar/Sidebar';
import SettingsModal from './settings/SettingsModal';
import { ChevronsRightIcon } from './ui/icons';
import { createDocumentsSurface } from './documents/DocumentsTable';
import { createPreviewSurface } from './preview/PreviewWorkspace';
@@ -338,12 +339,12 @@ const LoginView = ({
<div className="login-card__tenant-list">
{tenantSelection.tenants.map((tenant) => (
<button
key={tenant.tenant_id}
key={tenant.id}
type="button"
onClick={() => onSelectTenant?.(tenant)}
disabled={Boolean(selectingTenantId)}
className={
selectingTenantId === tenant.tenant_id
selectingTenantId === tenant.id
? 'login-card__tenant-button is-loading'
: 'login-card__tenant-button'
}
@@ -440,7 +441,7 @@ const AppLayout = () => {
const previewDocumentId = routeDocumentId;
const { status: appStatus, token, tenant, tenants: tenantOptions = [] } = appState;
const tenantSlug = tenant?.slug || null;
const currentTenantId = tenant?.tenant_id || null;
const currentTenantId = tenant?.id || null;
const [status, setStatus] = useState(null);
const setStatusMessage = useCallback((message, variant = 'info') => {
setStatus(message ? { message, variant } : null);
@@ -464,6 +465,7 @@ const AppLayout = () => {
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);
@@ -2004,38 +2006,6 @@ const AppLayout = () => {
],
);
const loadFolder = useCallback(
async (folderId, { showLoading = true, preserveSearch = false } = {}) => {
const targetId = folderId || 'root';
setSelectedFolder(targetId);
if (showLoading) setLoading(true);
try {
const contents = await ensureFolderData(targetId, { force: true, prefetchDepth: 1 });
if (targetId !== 'root') {
try {
await ensureFolderData('root', {
force: false,
includeDocuments: false,
prefetchDepth: 1,
});
} catch (error) {
console.warn('Failed to refresh root folder tree', error);
}
}
applySelectedFolder(targetId, contents);
if (!preserveSearch) {
setSearchResults(null);
}
} catch (error) {
notifyApiError(error, 'Failed to load folder contents.');
} finally {
if (showLoading) setLoading(false);
}
},
[ensureFolderData, applySelectedFolder, notifyApiError],
);
const expandFolderAncestors = useCallback(
(targetId) => {
if (!targetId || targetId === 'root') {
@@ -2097,51 +2067,73 @@ const AppLayout = () => {
return;
}
const visited = new Set();
const stack = [];
let currentId = targetId;
let guard = 0;
while (currentId && currentId !== 'root' && guard < 32) {
guard += 1;
if (visited.has(currentId)) {
break;
}
visited.add(currentId);
const node = folderNodes.get(currentId);
if (!node) {
stack.push(currentId);
break;
const fetchAncestor = async (folderId, guard = 0) => {
if (!folderId || folderId === 'root' || guard > 32) {
return;
}
const parentId = node.parentId ?? 'root';
if (!parentId || parentId === 'root') {
break;
const existing = folderNodes.get(folderId);
if (existing?.loaded) {
return;
}
stack.push(parentId);
currentId = parentId;
}
while (stack.length) {
const ancestorId = stack.pop();
if (!ancestorId || ancestorId === 'root') {
continue;
try {
const contents = await ensureFolderData(folderId, {
includeDocuments: false,
force: false,
prefetchDepth: 0,
});
const parentId = contents?.folder?.parent_id ?? 'root';
if (parentId && parentId !== 'root') {
await fetchAncestor(parentId, guard + 1);
}
} catch (error) {
console.warn('Failed to ensure ancestor folder for navigation', folderId, error);
}
if (!folderNodes.has(ancestorId)) {
};
await fetchAncestor(targetId, 0);
},
[folderNodes, ensureFolderData],
);
const loadFolder = useCallback(
async (folderId, { showLoading = true, preserveSearch = false } = {}) => {
const targetId = folderId || 'root';
setSelectedFolder(targetId);
await ensureFolderAncestorsLoaded(targetId);
expandFolderAncestors(targetId);
if (showLoading) setLoading(true);
try {
const contents = await ensureFolderData(targetId, { force: true, prefetchDepth: 1 });
if (targetId !== 'root') {
try {
await ensureFolderData(ancestorId, {
includeDocuments: false,
await ensureFolderData('root', {
force: false,
includeDocuments: false,
prefetchDepth: 1,
});
} catch (error) {
console.warn('Failed to ensure ancestor folder for navigation', ancestorId, error);
console.warn('Failed to refresh root folder tree', error);
}
}
applySelectedFolder(targetId, contents);
if (!preserveSearch) {
setSearchResults(null);
}
} catch (error) {
notifyApiError(error, 'Failed to load folder contents.');
} finally {
if (showLoading) setLoading(false);
}
},
[folderNodes, ensureFolderData],
[
ensureFolderData,
applySelectedFolder,
notifyApiError,
ensureFolderAncestorsLoaded,
expandFolderAncestors,
],
);
const selectFolder = useCallback(
@@ -2200,10 +2192,7 @@ const AppLayout = () => {
const hasData = folderContents.has(targetParam);
if (targetParam !== selectedFolder || !hasData) {
loadFolder(targetParam, {
showLoading: !routeDocumentId,
preserveSearch: isFilterActive,
});
selectFolder(targetParam, { immediate: true });
}
}, [
token,
@@ -2211,9 +2200,9 @@ const AppLayout = () => {
routeFolderId,
routeDocumentId,
selectedFolder,
loadFolder,
folderContents,
isFilterActive,
selectFolder,
]);
const refreshCurrentFolder = useCallback(async () => {
@@ -4048,6 +4037,14 @@ const AppLayout = () => {
setCorrespondentsModalOpen(false);
}, []);
const openSettingsModal = useCallback(() => {
setSettingsModalOpen(true);
}, []);
const closeSettingsModal = useCallback(() => {
setSettingsModalOpen(false);
}, []);
const handleCreateFolderSubmit = useCallback(
async (event) => {
event.preventDefault();
@@ -4111,10 +4108,11 @@ const AppLayout = () => {
useEffect(() => {
setTagsModalOpen(false);
setCorrespondentsModalOpen(false);
setSettingsModalOpen(false);
}, [location.pathname]);
useEffect(() => {
if (!isTagsModalOpen && !isCorrespondentsModalOpen) {
if (!isTagsModalOpen && !isCorrespondentsModalOpen && !isSettingsModalOpen) {
return;
}
const handleKeyDown = (event) => {
@@ -4124,6 +4122,8 @@ const AppLayout = () => {
closeTagsModal();
} else if (isCorrespondentsModalOpen) {
closeCorrespondentsModal();
} else if (isSettingsModalOpen) {
closeSettingsModal();
}
}
};
@@ -4131,7 +4131,14 @@ const AppLayout = () => {
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [isTagsModalOpen, isCorrespondentsModalOpen, closeTagsModal, closeCorrespondentsModal]);
}, [
isTagsModalOpen,
isCorrespondentsModalOpen,
isSettingsModalOpen,
closeTagsModal,
closeCorrespondentsModal,
closeSettingsModal,
]);
useEffect(() => {
if (!token) return undefined;
@@ -4943,8 +4950,7 @@ const AppLayout = () => {
const handleTenantSelect = useCallback(
async (tenantOption, { refreshOnly = false } = {}) => {
const requestedTenantId =
tenantOption?.tenant_id || tenantOption?.tenantId || tenantOption?.id || null;
const requestedTenantId = tenantOption?.id ?? null;
if (!refreshOnly && (!requestedTenantId || requestedTenantId === currentTenantId)) {
return;
}
@@ -4980,7 +4986,7 @@ const AppLayout = () => {
api.defaults.headers.common.Authorization = `Bearer ${data.access_token}`;
tokenRef.current = data.access_token;
tenantIdRef.current = data?.tenant?.tenant_id || null;
tenantIdRef.current = data?.tenant?.id ?? null;
if (Array.isArray(data?.tenants)) {
appDispatch({ type: 'SET_TENANTS', tenants: data.tenants });
@@ -4992,10 +4998,8 @@ const AppLayout = () => {
await Promise.all([refreshTags(), refreshCorrespondents()]);
await loadFolder('root', { showLoading: false, preserveSearch: false });
setStatusMessage(
`Switched to ${data?.tenant?.slug || data?.tenant?.tenant_id || 'tenant'}.`,
'info',
);
const tenantLabel = data?.tenant?.slug || data?.tenant?.id || 'tenant';
setStatusMessage(`Switched to ${tenantLabel}.`, 'info');
} catch (error) {
notifyApiError(error, 'Failed to switch tenant.');
} finally {
@@ -5102,6 +5106,7 @@ const AppLayout = () => {
tenants: tenantOptions,
activeTenantId: currentTenantId,
onSelectTenant: handleTenantSelect,
onOpenSettings: openSettingsModal,
};
const sidebarPropsWithActions = useMemo(
@@ -5215,6 +5220,7 @@ const AppLayout = () => {
resolveApiPath,
openTagsModal,
openCorrespondentsModal,
openSettingsModal,
}),
[
token,
@@ -5255,6 +5261,7 @@ const AppLayout = () => {
resolveApiPath,
openTagsModal,
openCorrespondentsModal,
openSettingsModal,
],
);
@@ -5397,6 +5404,7 @@ const AppLayout = () => {
</div>
</div>
)}
<SettingsModal open={isSettingsModalOpen} onClose={closeSettingsModal} />
</div>
</AppShellContext.Provider>
);
@@ -5661,15 +5669,15 @@ const LoginRoute = () => {
const handleTenantSelect = useCallback(
async (tenant) => {
if (!tenantSelection?.selectionToken || !tenant?.tenant_id) {
if (!tenantSelection?.selectionToken || !tenant?.id) {
return;
}
try {
setSelectingTenantId(tenant.tenant_id);
setSelectingTenantId(tenant.id);
const { data } = await api.post(
'/auth/select-tenant',
{ tenant_id: tenant.tenant_id },
{ tenant_id: tenant.id },
{
headers: {
Authorization: `Bearer ${tenantSelection.selectionToken}`,