frontend cleanup
This commit is contained in:
+91
-83
@@ -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}`,
|
||||
|
||||
Reference in New Issue
Block a user