linting, tenant slug -> name
This commit is contained in:
@@ -123,10 +123,11 @@ const readTransferData = (dataTransfer, mimeTypes) => {
|
||||
if (raw) {
|
||||
return raw;
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
if (DEBUG_DROP) {
|
||||
console.warn('[skeuo] readTransferData failed for type', type, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
@@ -141,10 +142,9 @@ const parseTagTransferPayload = (event) => {
|
||||
}
|
||||
try {
|
||||
return JSON.parse(raw);
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
console.warn('[skeuo] parseTagTransferPayload failed', error);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -465,10 +465,9 @@ const usePreviewMetadata = (documents, getDocumentAsset, ensureAssetUrl) => {
|
||||
view = createAssetView(asset);
|
||||
metadata = view.getPrimaryMetadata();
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
console.warn('[skeuo] ensureDocumentSize metadata fetch failed', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasDimensions(metadata)) {
|
||||
@@ -509,7 +508,7 @@ const usePreviewMetadata = (documents, getDocumentAsset, ensureAssetUrl) => {
|
||||
return metadataMap;
|
||||
};
|
||||
|
||||
const seededRandom = (input) => {
|
||||
function seededRandom(input) {
|
||||
const text = String(input);
|
||||
let hash = 2166136261;
|
||||
for (let index = 0; index < text.length; index += 1) {
|
||||
@@ -517,16 +516,18 @@ const seededRandom = (input) => {
|
||||
hash = Math.imul(hash, 16777619);
|
||||
}
|
||||
return (hash >>> 0) / 4294967295;
|
||||
};
|
||||
}
|
||||
|
||||
const randomRangeFromSeed = (seedKey, min, max) => {
|
||||
function randomRangeFromSeed(seedKey, min, max) {
|
||||
const span = max - min;
|
||||
if (span <= 0) return min;
|
||||
const seed = seededRandom(seedKey);
|
||||
return min + seed * span;
|
||||
};
|
||||
}
|
||||
|
||||
const buildKey = (docId, suffix) => `${docId}::${suffix}`;
|
||||
function buildKey(docId, suffix) {
|
||||
return `${docId}::${suffix}`;
|
||||
}
|
||||
|
||||
const clampCardDimensions = (width, height) => {
|
||||
const w = Number(width);
|
||||
@@ -758,10 +759,11 @@ const DesktopWorkspace = ({
|
||||
if (DEBUG_FOCUS) {
|
||||
console.log('[skeuo] focusCanvas: applied focus. activeElement:', document?.activeElement);
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
if (DEBUG_FOCUS) {
|
||||
console.warn('[skeuo] focusTarget failed to focus canvas', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof window === 'undefined') {
|
||||
@@ -1551,20 +1553,18 @@ const syncLayoutSnapshot = useCallback(() => {
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.effectAllowed = 'copyMove';
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
console.warn('[skeuo] Failed to set drag effect', error);
|
||||
}
|
||||
|
||||
const payload = JSON.stringify({ id: tag.id, label: tag.label, sourceDocId: doc.id });
|
||||
try {
|
||||
event.dataTransfer?.setData('application/x-papercrate-tag', payload);
|
||||
event.dataTransfer?.setData('text/papercrate-tag', payload);
|
||||
event.dataTransfer?.setData('text/plain', tag.label || 'Tag');
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
console.warn('[skeuo] Failed to populate drag data for tag', error);
|
||||
}
|
||||
|
||||
const pending = pendingDocTagDragRef.current;
|
||||
const node = event.currentTarget;
|
||||
@@ -1593,10 +1593,9 @@ const syncLayoutSnapshot = useCallback(() => {
|
||||
if (preview && event.dataTransfer) {
|
||||
try {
|
||||
event.dataTransfer.setDragImage(preview.clone, preview.offsetX, preview.offsetY);
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
console.warn('[skeuo] Failed to set drag image', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
draggingTagRef.current = {
|
||||
|
||||
@@ -38,10 +38,11 @@ function CorrespondentsPanel({
|
||||
try {
|
||||
await onUpdate(editingId, { name: trimmed });
|
||||
cancelEdit();
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
onNotify?.('Failed to update correspondent.', 'error');
|
||||
console.error('[correspondents] update failed', error);
|
||||
setSaving(false);
|
||||
}
|
||||
}, [editingId, draftName, onUpdate, cancelEdit, onNotify]);
|
||||
|
||||
const handleDelete = useCallback(
|
||||
@@ -53,16 +54,14 @@ function CorrespondentsPanel({
|
||||
if (editingId === correspondent.id) {
|
||||
cancelEdit();
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {
|
||||
return;
|
||||
} catch (error) {
|
||||
onNotify?.('Failed to delete correspondent.', 'error');
|
||||
console.error('[correspondents] delete failed', error);
|
||||
} finally {
|
||||
setDeletingId(null);
|
||||
}
|
||||
},
|
||||
[onDelete, editingId, cancelEdit],
|
||||
[onDelete, editingId, cancelEdit, onNotify],
|
||||
);
|
||||
|
||||
const handleCreate = useCallback(
|
||||
@@ -77,11 +76,9 @@ function CorrespondentsPanel({
|
||||
try {
|
||||
await onCreate({ name: trimmed });
|
||||
setCreateName('');
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {
|
||||
return;
|
||||
} catch (error) {
|
||||
onNotify?.('Failed to create correspondent.', 'error');
|
||||
console.error('[correspondents] create failed', error);
|
||||
} finally {
|
||||
setCreating(false);
|
||||
}
|
||||
|
||||
@@ -4,14 +4,12 @@ export const preventAll = (event) => {
|
||||
}
|
||||
try {
|
||||
event.preventDefault();
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
console.warn('[events] preventDefault failed', error);
|
||||
}
|
||||
try {
|
||||
event.stopPropagation();
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
console.warn('[events] stopPropagation failed', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -35,16 +35,17 @@ const useDocumentDrag = () => {
|
||||
if (capturedTarget && typeof capturedTarget.releasePointerCapture === 'function') {
|
||||
try {
|
||||
capturedTarget.releasePointerCapture(pointerId);
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
if (debugDrag) {
|
||||
console.warn('[skeuo] releasePointerCapture failed', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
dragStateRef.current = null;
|
||||
setDraggingId((current) => (current === state.docId ? null : current));
|
||||
syncLayoutSnapshot();
|
||||
},
|
||||
[setDraggingId, syncLayoutSnapshot],
|
||||
[debugDrag, setDraggingId, syncLayoutSnapshot],
|
||||
);
|
||||
|
||||
const handlePointerDown = useCallback(
|
||||
@@ -83,10 +84,11 @@ const useDocumentDrag = () => {
|
||||
if (capturedTarget && typeof capturedTarget.setPointerCapture === 'function') {
|
||||
try {
|
||||
capturedTarget.setPointerCapture(event.pointerId);
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
if (debugDrag) {
|
||||
console.warn('[skeuo] setPointerCapture failed', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
dragStateRef.current = {
|
||||
docId,
|
||||
|
||||
@@ -200,11 +200,7 @@ const PreviewStack = ({
|
||||
onOpenPreview,
|
||||
onZoomPreview,
|
||||
}) => {
|
||||
if (!items.length) {
|
||||
return <span className="meta">{emptyMessage}</span>;
|
||||
}
|
||||
|
||||
const limited = items.slice(0, maxItems);
|
||||
const limited = useMemo(() => items.slice(0, maxItems), [items, maxItems]);
|
||||
const hasMultiple = limited.length > 1;
|
||||
const preparedItems = useMemo(
|
||||
() =>
|
||||
@@ -215,6 +211,10 @@ const PreviewStack = ({
|
||||
[limited],
|
||||
);
|
||||
|
||||
if (!limited.length) {
|
||||
return <span className="meta">{emptyMessage}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="preview-stack preview-stack--stacked">
|
||||
{preparedItems.map(({ entry, angle }, index) => {
|
||||
@@ -329,6 +329,11 @@ const DetailPanel = ({
|
||||
const [titleError, setTitleError] = useState(null);
|
||||
const [zoomedPreview, setZoomedPreview] = useState(null);
|
||||
|
||||
const bulkDocumentIds = useMemo(
|
||||
() => selectedDocuments.map((doc) => doc?.id).filter(Boolean),
|
||||
[selectedDocuments],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!singleDoc) {
|
||||
setTitleEditDocId(null);
|
||||
@@ -1097,7 +1102,7 @@ const DetailPanel = ({
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
};
|
||||
const documentIds = Array.from(new Set(selectedDocuments.map((doc) => doc?.id).filter(Boolean)));
|
||||
const documentIds = bulkDocumentIds;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -1224,7 +1229,7 @@ const DetailPanel = ({
|
||||
className="icon-button ghost"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onBulkReanalyze(documentIds);
|
||||
onBulkReanalyze(bulkDocumentIds);
|
||||
}}
|
||||
aria-label="Re-run analysis for selection"
|
||||
title="Re-run analysis for selection"
|
||||
|
||||
@@ -392,10 +392,9 @@ const DocumentsTable = ({
|
||||
if (parsed?.id && onDocumentTagDrop) {
|
||||
onDocumentTagDrop(documentId, parsed);
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
console.warn('[documents] Failed to parse tag drop payload', error);
|
||||
}
|
||||
},
|
||||
[isTagDragEvent, onDocumentTagDrop],
|
||||
);
|
||||
@@ -714,10 +713,9 @@ const DocumentsTable = ({
|
||||
event.dataTransfer?.setData('application/x-papercrate-tag', payload);
|
||||
event.dataTransfer?.setData('text/papercrate-tag', payload);
|
||||
event.dataTransfer?.setData('text/plain', tag.label || 'Tag');
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
console.warn('[documents] Failed to configure tag drag payload', error);
|
||||
}
|
||||
}}
|
||||
onDragEnd={(event) => {
|
||||
event.stopPropagation();
|
||||
@@ -925,21 +923,20 @@ const DocumentsTable = ({
|
||||
onDragStart={(event) => {
|
||||
event.stopPropagation();
|
||||
try {
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.effectAllowed = 'copyMove';
|
||||
}
|
||||
const payload = JSON.stringify({
|
||||
id: tag.id,
|
||||
label: tag.label,
|
||||
sourceDocId: doc.id,
|
||||
});
|
||||
event.dataTransfer?.setData('application/x-papercrate-tag', payload);
|
||||
event.dataTransfer?.setData('text/papercrate-tag', payload);
|
||||
event.dataTransfer?.setData('text/plain', tag.label || 'Tag');
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.effectAllowed = 'copyMove';
|
||||
}
|
||||
const payload = JSON.stringify({
|
||||
id: tag.id,
|
||||
label: tag.label,
|
||||
sourceDocId: doc.id,
|
||||
});
|
||||
event.dataTransfer?.setData('application/x-papercrate-tag', payload);
|
||||
event.dataTransfer?.setData('text/papercrate-tag', payload);
|
||||
event.dataTransfer?.setData('text/plain', tag.label || 'Tag');
|
||||
} catch (error) {
|
||||
console.warn('[documents] Failed to configure tag drag payload', error);
|
||||
}
|
||||
}}
|
||||
onDragEnd={(event) => {
|
||||
event.stopPropagation();
|
||||
|
||||
+59
-219
@@ -53,10 +53,9 @@ if (storage) {
|
||||
if (rawTenant) {
|
||||
STORED_TENANT = JSON.parse(rawTenant);
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
console.warn('[app] Failed to parse stored tenant metadata', error);
|
||||
}
|
||||
}
|
||||
if (STORED_TOKEN) {
|
||||
api.defaults.headers.common.Authorization = `Bearer ${STORED_TOKEN}`;
|
||||
@@ -428,7 +427,7 @@ const LoginView = ({
|
||||
: 'login-card__tenant-button'
|
||||
}
|
||||
>
|
||||
{tenant.slug}
|
||||
{tenant.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -492,7 +491,7 @@ const AppLayout = () => {
|
||||
const routeDocumentId = docMatch?.params?.documentId || null;
|
||||
const previewDocumentId = routeDocumentId;
|
||||
const { status: appStatus, token, tenant, tenants: tenantOptions = [] } = appState;
|
||||
const tenantSlug = tenant?.slug || null;
|
||||
const tenantName = tenant?.name || tenant?.slug || null;
|
||||
const currentTenantId = tenant?.id || null;
|
||||
const [status, setStatus] = useState(null);
|
||||
const setStatusMessage = useCallback((message, variant = 'info') => {
|
||||
@@ -586,6 +585,8 @@ const AppLayout = () => {
|
||||
}
|
||||
}, [appDispatch]);
|
||||
const [searchResults, setSearchResults] = useState(null);
|
||||
const [previewEntries, setPreviewEntries] = useState(() => new Map());
|
||||
const previewInflightRef = useRef(new Map());
|
||||
const [tags, setTags] = useState([]);
|
||||
const [correspondents, setCorrespondents] = useState([]);
|
||||
const [webdavTokens, setWebdavTokens] = useState([]);
|
||||
@@ -596,6 +597,7 @@ const AppLayout = () => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [activeTagFilters, setActiveTagFilters] = useState([]);
|
||||
const [activeCorrespondentFilters, setActiveCorrespondentFilters] = useState([]);
|
||||
const [searchLoading, setSearchLoading] = useState(false);
|
||||
const documentsRouteMatch = useMatch('/documents');
|
||||
const documentsFolderRouteMatch = useMatch('/documents/folder/:folderId');
|
||||
const documentsDetailRouteMatch = useMatch('/documents/:documentId');
|
||||
@@ -656,7 +658,6 @@ const AppLayout = () => {
|
||||
folderName: DEFAULT_FOLDER_NAME,
|
||||
});
|
||||
const [activePreviewId, setActivePreviewId] = useState(routeDocumentId || null);
|
||||
const [searchLoading, setSearchLoading] = useState(false);
|
||||
const shellRef = useRef(null);
|
||||
const assetManagerRef = useRef(null);
|
||||
if (!assetManagerRef.current) {
|
||||
@@ -1951,6 +1952,21 @@ const AppLayout = () => {
|
||||
[refreshCorrespondents, notifyApiError, setStatusMessage, mapDocumentCaches],
|
||||
);
|
||||
|
||||
const refreshCurrentFolder = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const contents = await ensureFolderData(selectedFolder, {
|
||||
force: true,
|
||||
prefetchDepth: 1,
|
||||
});
|
||||
applySelectedFolder(selectedFolder, contents);
|
||||
} catch (error) {
|
||||
notifyApiError(error, 'Failed to refresh folder.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [selectedFolder, ensureFolderData, applySelectedFolder, notifyApiError]);
|
||||
|
||||
const handleDocumentCorrespondentAttach = useCallback(
|
||||
async ({ documentId, correspondentId }, { notify = true, refresh = true } = {}) => {
|
||||
if (!documentId || !correspondentId) {
|
||||
@@ -2033,10 +2049,10 @@ const AppLayout = () => {
|
||||
if (input) {
|
||||
input.value = '';
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
setStatusMessage('Failed to assign correspondent.', 'error');
|
||||
console.error('[documents] assign correspondent failed', error);
|
||||
}
|
||||
},
|
||||
[
|
||||
handleCorrespondentCreate,
|
||||
@@ -2286,21 +2302,6 @@ const AppLayout = () => {
|
||||
selectFolder,
|
||||
]);
|
||||
|
||||
const refreshCurrentFolder = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const contents = await ensureFolderData(selectedFolder, {
|
||||
force: true,
|
||||
prefetchDepth: 1,
|
||||
});
|
||||
applySelectedFolder(selectedFolder, contents);
|
||||
} catch (error) {
|
||||
notifyApiError(error, 'Failed to refresh folder.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [selectedFolder, ensureFolderData, applySelectedFolder, notifyApiError]);
|
||||
|
||||
const resolveTargetDocumentIds = useCallback(
|
||||
(candidateIds) => {
|
||||
const normalized = Array.isArray(candidateIds)
|
||||
@@ -2671,8 +2672,6 @@ const AppLayout = () => {
|
||||
);
|
||||
|
||||
const folderPathCacheRef = useRef(new Map());
|
||||
const [previewEntries, setPreviewEntries] = useState(() => new Map());
|
||||
const previewInflightRef = useRef(new Map());
|
||||
|
||||
const ensureFolderPathOnServer = useCallback(
|
||||
async (baseFolderId, segments) => {
|
||||
@@ -2919,10 +2918,9 @@ const AppLayout = () => {
|
||||
event.dataTransfer.setData('application/x-papercrate-folder', folderSelection[0]);
|
||||
}
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
console.warn('[documents] Failed to populate drag payload', error);
|
||||
}
|
||||
if (previewNode) {
|
||||
const width = previewNode.offsetWidth || 96;
|
||||
const height = previewNode.offsetHeight || 96;
|
||||
@@ -2993,10 +2991,9 @@ const AppLayout = () => {
|
||||
JSON.stringify(effectiveDocumentSelection),
|
||||
);
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
console.warn('[folders] Failed to set drag payload', error);
|
||||
}
|
||||
|
||||
const previewDocs = effectiveDocumentSelection
|
||||
.map((id) => documentLookup.get(id) || null)
|
||||
@@ -3112,14 +3109,14 @@ const AppLayout = () => {
|
||||
|
||||
const readAllEntries = async (reader) => {
|
||||
const entries = [];
|
||||
while (true) {
|
||||
let batch = [];
|
||||
do {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const batch = await new Promise((resolve, reject) => reader.readEntries(resolve, reject));
|
||||
if (!batch.length) {
|
||||
break;
|
||||
batch = await new Promise((resolve, reject) => reader.readEntries(resolve, reject));
|
||||
if (batch.length) {
|
||||
entries.push(...batch);
|
||||
}
|
||||
entries.push(...batch);
|
||||
}
|
||||
} while (batch.length);
|
||||
return entries;
|
||||
};
|
||||
|
||||
@@ -4424,10 +4421,9 @@ const AppLayout = () => {
|
||||
folderIds = parsed.filter(Boolean);
|
||||
}
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
console.warn('[folders] Failed to parse folder list drag payload', error);
|
||||
}
|
||||
|
||||
if (!folderIds.length) {
|
||||
let folderSourceId = draggedFolderId;
|
||||
@@ -4436,10 +4432,9 @@ const AppLayout = () => {
|
||||
if (event.dataTransfer.types?.includes('application/x-papercrate-folder')) {
|
||||
folderSourceId = event.dataTransfer.getData('application/x-papercrate-folder');
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
console.warn('[folders] Failed to read folder id from drag payload', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (folderSourceId) {
|
||||
@@ -4480,21 +4475,19 @@ const AppLayout = () => {
|
||||
docIds = parsed.filter(Boolean);
|
||||
}
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
} catch (error) {
|
||||
console.warn('[documents] Failed to parse document list drag payload', error);
|
||||
}
|
||||
|
||||
if (!docIds.length) {
|
||||
try {
|
||||
const single = event.dataTransfer.getData('application/x-papercrate-doc');
|
||||
if (single) {
|
||||
docIds = [single];
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
try {
|
||||
const single = event.dataTransfer.getData('application/x-papercrate-doc');
|
||||
if (single) {
|
||||
docIds = [single];
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[documents] Failed to read single document drag payload', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!docIds.length && draggedDocumentIds.length) {
|
||||
@@ -4551,159 +4544,6 @@ const AppLayout = () => {
|
||||
setStatusMessage,
|
||||
],
|
||||
);
|
||||
const node = folderNodes.get(folderId);
|
||||
const nextExpanded = !(node?.expanded ?? false);
|
||||
if (nextExpanded) {
|
||||
try {
|
||||
await ensureFolderData(folderId, {
|
||||
includeDocuments: false,
|
||||
prefetchDepth: 1,
|
||||
});
|
||||
} catch (error) {
|
||||
notifyApiError(error, 'Failed to load folder.');
|
||||
}
|
||||
} else if (node && !node.loaded) {
|
||||
try {
|
||||
await ensureFolderData(folderId, {
|
||||
includeDocuments: false,
|
||||
prefetchDepth: 1,
|
||||
});
|
||||
} catch (error) {
|
||||
notifyApiError(error, 'Failed to load folder.');
|
||||
}
|
||||
}
|
||||
setFolderNodes((prev) => {
|
||||
const next = new Map(prev);
|
||||
const current = next.get(folderId);
|
||||
if (!current) return prev;
|
||||
next.set(folderId, { ...current, expanded: nextExpanded });
|
||||
return next;
|
||||
});
|
||||
},
|
||||
onSelect: selectFolder,
|
||||
onDrop: async (event, folderId) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.currentTarget.classList.remove('is-drop-target');
|
||||
|
||||
let folderIds = [];
|
||||
try {
|
||||
const rawFolderList = event.dataTransfer.getData('application/x-papercrate-folder-list');
|
||||
if (rawFolderList) {
|
||||
const parsed = JSON.parse(rawFolderList);
|
||||
if (Array.isArray(parsed)) {
|
||||
folderIds = parsed.filter(Boolean);
|
||||
}
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
|
||||
if (!folderIds.length) {
|
||||
let folderSourceId = draggedFolderId;
|
||||
if (!folderSourceId) {
|
||||
try {
|
||||
if (event.dataTransfer.types?.includes('application/x-papercrate-folder')) {
|
||||
folderSourceId = event.dataTransfer.getData('application/x-papercrate-folder');
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
}
|
||||
|
||||
if (folderSourceId) {
|
||||
folderIds = [folderSourceId];
|
||||
}
|
||||
}
|
||||
|
||||
folderIds = Array.from(new Set(folderIds.filter(Boolean)));
|
||||
|
||||
if (folderIds.length) {
|
||||
setDraggedFolderId(null);
|
||||
const invalidMove = folderIds.some((sourceId) => isInvalidFolderDrop(sourceId, folderId));
|
||||
if (invalidMove) {
|
||||
setStatusMessage(
|
||||
'Cannot move a folder into itself or one of its descendants.',
|
||||
'error',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const sourceId of folderIds) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await moveFolder(sourceId, folderId);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasFiles(event)) {
|
||||
await handleFileDrop(event.dataTransfer, folderId);
|
||||
return;
|
||||
}
|
||||
|
||||
let docIds = [];
|
||||
try {
|
||||
const raw = event.dataTransfer.getData('application/x-papercrate-doc-list');
|
||||
if (raw) {
|
||||
const parsed = JSON.parse(raw);
|
||||
if (Array.isArray(parsed)) {
|
||||
docIds = parsed.filter(Boolean);
|
||||
}
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
|
||||
if (!docIds.length) {
|
||||
try {
|
||||
const single = event.dataTransfer.getData('application/x-papercrate-doc');
|
||||
if (single) {
|
||||
docIds = [single];
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
}
|
||||
|
||||
if (!docIds.length && draggedDocumentIds.length) {
|
||||
docIds = draggedDocumentIds;
|
||||
}
|
||||
|
||||
docIds = Array.from(new Set(docIds));
|
||||
|
||||
if (!docIds.length || folderId === selectedFolder) {
|
||||
return;
|
||||
}
|
||||
|
||||
setDraggedDocumentIds([]);
|
||||
await moveDocumentsToFolder(docIds, folderId);
|
||||
},
|
||||
onDragOver: (event, folderId) => {
|
||||
const folderDragActive = Boolean(draggedFolderId);
|
||||
if (folderDragActive && isInvalidFolderDrop(draggedFolderId, folderId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasFiles(event)) {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = 'copy';
|
||||
event.currentTarget.classList.add('is-drop-target');
|
||||
return;
|
||||
}
|
||||
|
||||
if (draggedDocumentIds.length || folderDragActive) {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = 'move';
|
||||
event.currentTarget.classList.add('is-drop-target');
|
||||
}
|
||||
},
|
||||
onDragLeave: (event) => {
|
||||
event.currentTarget.classList.remove('is-drop-target');
|
||||
},
|
||||
};
|
||||
|
||||
const selectedDocument = useMemo(() => {
|
||||
if (!focusedDocumentId) {
|
||||
@@ -5163,7 +5003,7 @@ const AppLayout = () => {
|
||||
await Promise.all([refreshTags(), refreshCorrespondents()]);
|
||||
await loadFolder('root', { showLoading: false, preserveSearch: false });
|
||||
|
||||
const tenantLabel = data?.tenant?.slug || data?.tenant?.id || 'tenant';
|
||||
const tenantLabel = data?.tenant?.name || data?.tenant?.id || 'tenant';
|
||||
setStatusMessage(`Switched to ${tenantLabel}.`, 'info');
|
||||
} catch (error) {
|
||||
notifyApiError(error, 'Failed to switch tenant.');
|
||||
@@ -5314,7 +5154,7 @@ const AppLayout = () => {
|
||||
isFilterActive,
|
||||
onLogout: handleLogout,
|
||||
status,
|
||||
tenantSlug,
|
||||
tenantName,
|
||||
tenants: tenantOptions,
|
||||
activeTenantId: currentTenantId,
|
||||
onSelectTenant: handleTenantSelect,
|
||||
@@ -5347,7 +5187,7 @@ const AppLayout = () => {
|
||||
status,
|
||||
tags,
|
||||
tenantOptions,
|
||||
tenantSlug,
|
||||
tenantName,
|
||||
toggleCorrespondentFilter,
|
||||
toggleTagFilter,
|
||||
],
|
||||
|
||||
@@ -154,7 +154,7 @@ const Sidebar = ({
|
||||
onLogout,
|
||||
status,
|
||||
onCollapse,
|
||||
tenantSlug,
|
||||
tenantName,
|
||||
tenants = [],
|
||||
activeTenantId = null,
|
||||
onSelectTenant,
|
||||
@@ -308,7 +308,7 @@ const Sidebar = ({
|
||||
>
|
||||
<span className="sidebar__title">
|
||||
Papercrate
|
||||
{tenantSlug ? <span className="sidebar__tenant"> / {tenantSlug}</span> : null}
|
||||
{tenantName ? <span className="sidebar__tenant"> / {tenantName}</span> : null}
|
||||
</span>
|
||||
<ChevronDownIcon
|
||||
className={`sidebar__title-chevron${tenantMenuOpen ? ' is-open' : ''}`}
|
||||
@@ -324,9 +324,10 @@ const Sidebar = ({
|
||||
tenants.map((tenant) => {
|
||||
const tenantId = tenant?.id || null;
|
||||
const isActive = tenantId === activeTenantId;
|
||||
const tenantLabel = tenant?.name || tenant?.slug || tenantId || 'Tenant';
|
||||
return (
|
||||
<button
|
||||
key={tenantId || tenant?.slug || tenant?.name}
|
||||
key={tenantId || tenantLabel}
|
||||
type="button"
|
||||
className={`menu__item${isActive ? ' active' : ''}`}
|
||||
onClick={() => handleTenantSelect(tenant)}
|
||||
@@ -335,9 +336,7 @@ const Sidebar = ({
|
||||
<span className="menu__check-slot">
|
||||
{isActive ? <CheckIcon size={16} /> : null}
|
||||
</span>
|
||||
<span className="menu__label">
|
||||
{tenant?.slug || tenant?.name || tenantId || 'Tenant'}
|
||||
</span>
|
||||
<span className="menu__label">{tenantLabel}</span>
|
||||
</button>
|
||||
);
|
||||
})
|
||||
@@ -437,26 +436,25 @@ const Sidebar = ({
|
||||
style={style || undefined}
|
||||
onClick={() => handleToggleTag(tag.id)}
|
||||
aria-pressed={isActive}
|
||||
draggable
|
||||
onDragStart={(event) => {
|
||||
try {
|
||||
const payload = JSON.stringify({
|
||||
id: tag.id,
|
||||
label: tag.label,
|
||||
color: tag.color || null,
|
||||
});
|
||||
event.dataTransfer.effectAllowed = 'copy';
|
||||
event.dataTransfer.setData('application/x-papercrate-tag', payload);
|
||||
event.dataTransfer.setData('text/papercrate-tag', payload);
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
}}
|
||||
>
|
||||
{tag.label}
|
||||
</button>
|
||||
);
|
||||
draggable
|
||||
onDragStart={(event) => {
|
||||
try {
|
||||
const payload = JSON.stringify({
|
||||
id: tag.id,
|
||||
label: tag.label,
|
||||
color: tag.color || null,
|
||||
});
|
||||
event.dataTransfer.effectAllowed = 'copy';
|
||||
event.dataTransfer.setData('application/x-papercrate-tag', payload);
|
||||
event.dataTransfer.setData('text/papercrate-tag', payload);
|
||||
} catch (error) {
|
||||
console.warn('[sidebar] Failed to set tag drag payload', error);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{tag.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,15 +23,11 @@ export async function resolveOcrTextUrl({
|
||||
let asset = pickAsset(docRef, getDocumentAsset);
|
||||
|
||||
if (!asset && typeof ensurePreviewData === 'function') {
|
||||
try {
|
||||
const refreshed = await ensurePreviewData(docRef.id);
|
||||
if (refreshed) {
|
||||
docRef = refreshed;
|
||||
}
|
||||
asset = pickAsset(docRef, getDocumentAsset);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
const refreshed = await ensurePreviewData(docRef.id);
|
||||
if (refreshed) {
|
||||
docRef = refreshed;
|
||||
}
|
||||
asset = pickAsset(docRef, getDocumentAsset);
|
||||
}
|
||||
|
||||
if (!asset) {
|
||||
|
||||
Reference in New Issue
Block a user