feat: add folder organization support to collections - #3143
Conversation
הוספת אפשרות לארגן קבצים בתוך אוסף לתיקיות (רמה אחת). קובץ יכול להופיע גם ב-root וגם בתיקיה (מקומות מרובים). תיקיות מוצגות כקבוצות מתקפלות (accordion) עם drag & drop. - Database: שדה folder ב-collection_items, מטאדאטה folders ב-user_collections - API: endpoints חדשים ל-CRUD תיקיות (create/rename/delete/reorder) - Frontend: רינדור accordion, מודאל יצירת תיקיה, inline rename, drag & drop - CSS: סגנונות לתיקיות מתקפלות עם RTL ו-responsive - Shared view: תצוגת תיקיות בצפייה ציבורית (read-only) https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
🧯 Dangerous deletes guard reportPolicy: see .cursorrules — dangerous deletions are blocked unless wrapped safely. Summary:
Flagged findings (file:line:snippet): Excluded matches (by path pattern) |
⏱️ Performance report(No performance test durations collected. Mark tests with |
📖 Documentation PreviewThe documentation has been built successfully!
To view locally:
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
1. Mixed mode dedup: מפתח dedup חזר ל-2-tuple (source, file_name) בלבד
כדי למנוע כפילויות כשקובץ ידני בתיקיה ומחושב ב-root
2. Route conflict: הוספת ולידציה לשמות תיקיות שמורים ("reorder")
כדי למנוע התנגשות עם endpoint סידור תיקיות
3. Drag reorder: איסוף פריטים מכל ה-sections (root + תיקיות)
כדי לא לאבד פריטים מתיקיות אחרות בעת שליחת סדר לשרת
https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
1. Missing migration backfill: פריטים קיימים ללא folder מקבלים "" (root) כדי למנוע אי-התאמה בין null ל-"" באינדקס MongoDB 2. Cross-folder drag metadata: endpoint חדש move_item_folder שמעביר פריט בין תיקיות עם שמירת כל המטאדאטה (notes, tags, pinned) במקום add+remove שמאבד מידע 3. Root drop zone: container ה-root מרונדר תמיד (גם כשריק) כדי שתמיד יהיה אזור drop לגרירת פריטים חזרה ל-root https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
move_item_folder החזיר str(e) ישירות למשתמש — חשיפת מידע רגיש. מוחלף בהודעת שגיאה גנרית + לוג פנימי. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
1. Empty state: כשאין פריטים ואין תיקיות, buildFolderGroupedHtml מחזיר ריק כדי שהודעת "אין פריטים" תוצג כמו לפני השינוי 2. Folder drop race: סימון dropInProgress בעת drop על תיקיה/root כדי שדragend לא ישלח reorder מיותר עם סדר ישן 3. Add-folder button: מוסתר ב-workspace collections כי תיקיות לא רלוונטיות שם https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
When moving an item to a folder where the same file already exists, the unique index throws DuplicateKeyError. Now catches it gracefully by removing the source item (target already has the file). https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
The workspace board's remove handler was missing the folder field in the removeItems payload, while the regular view handler included it. This could cause silent removal failures for items in non-root folders. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
…olders 1. move_item_folder: on DuplicateKeyError, merge source metadata (note, tags, pinned, workspace_state) into target before deleting the source item, preventing silent metadata loss. 2. Mixed-mode dedup: manual items no longer deduplicated against each other across folders. The seen set now only filters computed items that duplicate manual ones. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
The truthiness check 'if val:' skipped merging pinned=False and empty tags/notes. Changed to 'is not None' to handle boolean and empty-string fields correctly. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
…ection drag 1. move_item_folder: call _refresh_collection_counts after deleting source item on DuplicateKeyError so items_count stays accurate. 2. Cross-collection sidebar drag: strip folder from payload when adding item to a different collection, preventing orphaned folders in the target collection. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
1. Folder/root drop handlers: set dropInProgress only after validating drag data and checking same-folder no-op, preventing leaked drag state on cancelled drops. 2. rename_folder fallback loop: wrap each update_one in its own try/except so a single DuplicateKeyError doesn't abort remaining items. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
…data 1. Add DuplicateKeyError stub class in the pymongo-unavailable fallback block to prevent NameError in move_item_folder. 2. Metadata merge on duplicate move now only copies meaningful values (non-empty note, non-empty tags, pinned=True, non-empty workspace_state) to avoid overwriting destination metadata with source defaults. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
workspace_state is a string (e.g. "todo", "in_progress", "done"), not a dict. The isinstance(dict) check was always False, silently dropping workspace_state during duplicate-key folder moves. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
…ailure 1. create_folder/update_folder: replace sanitize_input (which calls html.escape) with plain strip+truncate. The DB layer validates folder names, and the frontend escapes on render — no need to HTML-encode before storage. 2. Folder drop handlers: wrap moveItemFolder calls in try/finally with clearActiveDragContext() to prevent leaked drag state when the API call fails. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
After the rename fallback loop, any items still carrying the old folder name (due to DuplicateKeyError or other failures) are moved to root folder to prevent orphaned items pointing to a non-existent folder in the collection metadata. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
Added class-level _migration_done flag so the drop_index and update_many backfill operations only execute on first instantiation, not on every API request. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
Fetch the destination item before merging so that existing metadata (note, tags, pinned, workspace_state) on the target is preserved. Source values are only copied when the target field is empty/missing. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
The touch endHandler in wireDnd was not updated with folder support. Now traverses up to #collectionItems, uses the correct selector for all sections, and includes folder field in the reorder payload. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 74bca69. Configure here.
* feat: תמיכה בתיקיות בתוך אוספים (Collections Folders) הוספת אפשרות לארגן קבצים בתוך אוסף לתיקיות (רמה אחת). קובץ יכול להופיע גם ב-root וגם בתיקיה (מקומות מרובים). תיקיות מוצגות כקבוצות מתקפלות (accordion) עם drag & drop. - Database: שדה folder ב-collection_items, מטאדאטה folders ב-user_collections - API: endpoints חדשים ל-CRUD תיקיות (create/rename/delete/reorder) - Frontend: רינדור accordion, מודאל יצירת תיקיה, inline rename, drag & drop - CSS: סגנונות לתיקיות מתקפלות עם RTL ו-responsive - Shared view: תצוגת תיקיות בצפייה ציבורית (read-only) https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: תיקון 3 באגים בתיקיות אוספים (Bugbot PR #3143) 1. Mixed mode dedup: מפתח dedup חזר ל-2-tuple (source, file_name) בלבד כדי למנוע כפילויות כשקובץ ידני בתיקיה ומחושב ב-root 2. Route conflict: הוספת ולידציה לשמות תיקיות שמורים ("reorder") כדי למנוע התנגשות עם endpoint סידור תיקיות 3. Drag reorder: איסוף פריטים מכל ה-sections (root + תיקיות) כדי לא לאבד פריטים מתיקיות אחרות בעת שליחת סדר לשרת https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: תיקון 3 באגים נוספים בתיקיות אוספים (Bugbot סבב 2) 1. Missing migration backfill: פריטים קיימים ללא folder מקבלים "" (root) כדי למנוע אי-התאמה בין null ל-"" באינדקס MongoDB 2. Cross-folder drag metadata: endpoint חדש move_item_folder שמעביר פריט בין תיקיות עם שמירת כל המטאדאטה (notes, tags, pinned) במקום add+remove שמאבד מידע 3. Root drop zone: container ה-root מרונדר תמיד (גם כשריק) כדי שתמיד יהיה אזור drop לגרירת פריטים חזרה ל-root https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: הסתרת פרטי exception מתגובת API (CodeQL) move_item_folder החזיר str(e) ישירות למשתמש — חשיפת מידע רגיש. מוחלף בהודעת שגיאה גנרית + לוג פנימי. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: תיקון 3 באגים נוספים בתיקיות אוספים (Bugbot סבב 3) 1. Empty state: כשאין פריטים ואין תיקיות, buildFolderGroupedHtml מחזיר ריק כדי שהודעת "אין פריטים" תוצג כמו לפני השינוי 2. Folder drop race: סימון dropInProgress בעת drop על תיקיה/root כדי שדragend לא ישלח reorder מיותר עם סדר ישן 3. Add-folder button: מוסתר ב-workspace collections כי תיקיות לא רלוונטיות שם https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: handle DuplicateKeyError in move_item_folder When moving an item to a folder where the same file already exists, the unique index throws DuplicateKeyError. Now catches it gracefully by removing the source item (target already has the file). https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: add missing folder field to workspace remove payload The workspace board's remove handler was missing the folder field in the removeItems payload, while the regular view handler included it. This could cause silent removal failures for items in non-root folders. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: merge metadata on duplicate move & allow same file in multiple folders 1. move_item_folder: on DuplicateKeyError, merge source metadata (note, tags, pinned, workspace_state) into target before deleting the source item, preventing silent metadata loss. 2. Mixed-mode dedup: manual items no longer deduplicated against each other across folders. The seen set now only filters computed items that duplicate manual ones. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: use 'is not None' for metadata merge in move_item_folder The truthiness check 'if val:' skipped merging pinned=False and empty tags/notes. Changed to 'is not None' to handle boolean and empty-string fields correctly. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: refresh counts after duplicate move & strip folder on cross-collection drag 1. move_item_folder: call _refresh_collection_counts after deleting source item on DuplicateKeyError so items_count stays accurate. 2. Cross-collection sidebar drag: strip folder from payload when adding item to a different collection, preventing orphaned folders in the target collection. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: move dropInProgress after validation & resilient rename fallback 1. Folder/root drop handlers: set dropInProgress only after validating drag data and checking same-folder no-op, preventing leaked drag state on cancelled drops. 2. rename_folder fallback loop: wrap each update_one in its own try/except so a single DuplicateKeyError doesn't abort remaining items. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: add DuplicateKeyError fallback stub & only merge meaningful metadata 1. Add DuplicateKeyError stub class in the pymongo-unavailable fallback block to prevent NameError in move_item_folder. 2. Metadata merge on duplicate move now only copies meaningful values (non-empty note, non-empty tags, pinned=True, non-empty workspace_state) to avoid overwriting destination metadata with source defaults. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: correct workspace_state type check from dict to str in merge workspace_state is a string (e.g. "todo", "in_progress", "done"), not a dict. The isinstance(dict) check was always False, silently dropping workspace_state during duplicate-key folder moves. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: prevent double HTML-escaping of folder names & cleanup drag on failure 1. create_folder/update_folder: replace sanitize_input (which calls html.escape) with plain strip+truncate. The DB layer validates folder names, and the frontend escapes on render — no need to HTML-encode before storage. 2. Folder drop handlers: wrap moveItemFolder calls in try/finally with clearActiveDragContext() to prevent leaked drag state when the API call fails. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: move orphaned items to root after folder rename failures After the rename fallback loop, any items still carrying the old folder name (due to DuplicateKeyError or other failures) are moved to root folder to prevent orphaned items pointing to a non-existent folder in the collection metadata. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: run backfill migration only once per process Added class-level _migration_done flag so the drop_index and update_many backfill operations only execute on first instantiation, not on every API request. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: only merge source metadata into target when target field is empty Fetch the destination item before merging so that existing metadata (note, tags, pinned, workspace_state) on the target is preserved. Source values are only copied when the target field is empty/missing. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: add folder data to touch handler reorder payload The touch endHandler in wireDnd was not updated with folder support. Now traverses up to #collectionItems, uses the correct selector for all sections, and includes folder field in the reorder payload. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: improve folder drag-and-drop reliability 1. Fix dragleave flicker on folder headers by checking relatedTarget (same pattern used by sidebar drop handlers). 2. Set dropEffect='move' on dragover for visual feedback. 3. Make entire card draggable (not just the handle) so users can drag from anywhere on the card to move it between folders. 4. Add stopPropagation on drop to prevent details/summary toggle. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: prevent duplicate folder event handlers & add dragend on card 1. Use AbortController in wireFolderEvents to remove all prior listeners before re-registering on each render, preventing duplicate click/drag handlers from stacking up. 2. Add dragend handler on the card element (not just the handle) so drag cleanup runs correctly when dragging from card body. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G --------- Co-authored-by: Claude <noreply@anthropic.com>
* feat: תמיכה בתיקיות בתוך אוספים (Collections Folders) הוספת אפשרות לארגן קבצים בתוך אוסף לתיקיות (רמה אחת). קובץ יכול להופיע גם ב-root וגם בתיקיה (מקומות מרובים). תיקיות מוצגות כקבוצות מתקפלות (accordion) עם drag & drop. - Database: שדה folder ב-collection_items, מטאדאטה folders ב-user_collections - API: endpoints חדשים ל-CRUD תיקיות (create/rename/delete/reorder) - Frontend: רינדור accordion, מודאל יצירת תיקיה, inline rename, drag & drop - CSS: סגנונות לתיקיות מתקפלות עם RTL ו-responsive - Shared view: תצוגת תיקיות בצפייה ציבורית (read-only) https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: תיקון 3 באגים בתיקיות אוספים (Bugbot PR #3143) 1. Mixed mode dedup: מפתח dedup חזר ל-2-tuple (source, file_name) בלבד כדי למנוע כפילויות כשקובץ ידני בתיקיה ומחושב ב-root 2. Route conflict: הוספת ולידציה לשמות תיקיות שמורים ("reorder") כדי למנוע התנגשות עם endpoint סידור תיקיות 3. Drag reorder: איסוף פריטים מכל ה-sections (root + תיקיות) כדי לא לאבד פריטים מתיקיות אחרות בעת שליחת סדר לשרת https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: תיקון 3 באגים נוספים בתיקיות אוספים (Bugbot סבב 2) 1. Missing migration backfill: פריטים קיימים ללא folder מקבלים "" (root) כדי למנוע אי-התאמה בין null ל-"" באינדקס MongoDB 2. Cross-folder drag metadata: endpoint חדש move_item_folder שמעביר פריט בין תיקיות עם שמירת כל המטאדאטה (notes, tags, pinned) במקום add+remove שמאבד מידע 3. Root drop zone: container ה-root מרונדר תמיד (גם כשריק) כדי שתמיד יהיה אזור drop לגרירת פריטים חזרה ל-root https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: הסתרת פרטי exception מתגובת API (CodeQL) move_item_folder החזיר str(e) ישירות למשתמש — חשיפת מידע רגיש. מוחלף בהודעת שגיאה גנרית + לוג פנימי. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: תיקון 3 באגים נוספים בתיקיות אוספים (Bugbot סבב 3) 1. Empty state: כשאין פריטים ואין תיקיות, buildFolderGroupedHtml מחזיר ריק כדי שהודעת "אין פריטים" תוצג כמו לפני השינוי 2. Folder drop race: סימון dropInProgress בעת drop על תיקיה/root כדי שדragend לא ישלח reorder מיותר עם סדר ישן 3. Add-folder button: מוסתר ב-workspace collections כי תיקיות לא רלוונטיות שם https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: handle DuplicateKeyError in move_item_folder When moving an item to a folder where the same file already exists, the unique index throws DuplicateKeyError. Now catches it gracefully by removing the source item (target already has the file). https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: add missing folder field to workspace remove payload The workspace board's remove handler was missing the folder field in the removeItems payload, while the regular view handler included it. This could cause silent removal failures for items in non-root folders. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: merge metadata on duplicate move & allow same file in multiple folders 1. move_item_folder: on DuplicateKeyError, merge source metadata (note, tags, pinned, workspace_state) into target before deleting the source item, preventing silent metadata loss. 2. Mixed-mode dedup: manual items no longer deduplicated against each other across folders. The seen set now only filters computed items that duplicate manual ones. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: use 'is not None' for metadata merge in move_item_folder The truthiness check 'if val:' skipped merging pinned=False and empty tags/notes. Changed to 'is not None' to handle boolean and empty-string fields correctly. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: refresh counts after duplicate move & strip folder on cross-collection drag 1. move_item_folder: call _refresh_collection_counts after deleting source item on DuplicateKeyError so items_count stays accurate. 2. Cross-collection sidebar drag: strip folder from payload when adding item to a different collection, preventing orphaned folders in the target collection. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: move dropInProgress after validation & resilient rename fallback 1. Folder/root drop handlers: set dropInProgress only after validating drag data and checking same-folder no-op, preventing leaked drag state on cancelled drops. 2. rename_folder fallback loop: wrap each update_one in its own try/except so a single DuplicateKeyError doesn't abort remaining items. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: add DuplicateKeyError fallback stub & only merge meaningful metadata 1. Add DuplicateKeyError stub class in the pymongo-unavailable fallback block to prevent NameError in move_item_folder. 2. Metadata merge on duplicate move now only copies meaningful values (non-empty note, non-empty tags, pinned=True, non-empty workspace_state) to avoid overwriting destination metadata with source defaults. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: correct workspace_state type check from dict to str in merge workspace_state is a string (e.g. "todo", "in_progress", "done"), not a dict. The isinstance(dict) check was always False, silently dropping workspace_state during duplicate-key folder moves. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: prevent double HTML-escaping of folder names & cleanup drag on failure 1. create_folder/update_folder: replace sanitize_input (which calls html.escape) with plain strip+truncate. The DB layer validates folder names, and the frontend escapes on render — no need to HTML-encode before storage. 2. Folder drop handlers: wrap moveItemFolder calls in try/finally with clearActiveDragContext() to prevent leaked drag state when the API call fails. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: move orphaned items to root after folder rename failures After the rename fallback loop, any items still carrying the old folder name (due to DuplicateKeyError or other failures) are moved to root folder to prevent orphaned items pointing to a non-existent folder in the collection metadata. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: run backfill migration only once per process Added class-level _migration_done flag so the drop_index and update_many backfill operations only execute on first instantiation, not on every API request. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: only merge source metadata into target when target field is empty Fetch the destination item before merging so that existing metadata (note, tags, pinned, workspace_state) on the target is preserved. Source values are only copied when the target field is empty/missing. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: add folder data to touch handler reorder payload The touch endHandler in wireDnd was not updated with folder support. Now traverses up to #collectionItems, uses the correct selector for all sections, and includes folder field in the reorder payload. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: improve folder drag-and-drop reliability 1. Fix dragleave flicker on folder headers by checking relatedTarget (same pattern used by sidebar drop handlers). 2. Set dropEffect='move' on dragover for visual feedback. 3. Make entire card draggable (not just the handle) so users can drag from anywhere on the card to move it between folders. 4. Add stopPropagation on drop to prevent details/summary toggle. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: prevent duplicate folder event handlers & add dragend on card 1. Use AbortController in wireFolderEvents to remove all prior listeners before re-registering on each render, preventing duplicate click/drag handlers from stacking up. 2. Add dragend handler on the card element (not just the handle) so drag cleanup runs correctly when dragging from card body. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: prevent double-firing drag events from nested draggable elements Remove draggable="true" from the handle span and keep only on the article card. Remove handle-specific dragstart/dragend listeners, keeping only the card-level ones. This prevents event bubbling from causing duplicate API reorder calls. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: resolve IndexOptionsConflict in rules_storage index creation rules_storage.py was creating indexes without explicit names, which conflicted with the same indexes created by database/manager.py with name 'visual_rules_enabled_idx'. Now uses matching explicit names and downgrades code 85/86 conflicts to debug level. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: wrap each create_index in its own try-except to avoid skipping remaining indexes A single try block meant an early index failure (e.g. name conflict) would silently prevent all subsequent indexes from being created. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G --------- Co-authored-by: Claude <noreply@anthropic.com>
* feat: תמיכה בתיקיות בתוך אוספים (Collections Folders) הוספת אפשרות לארגן קבצים בתוך אוסף לתיקיות (רמה אחת). קובץ יכול להופיע גם ב-root וגם בתיקיה (מקומות מרובים). תיקיות מוצגות כקבוצות מתקפלות (accordion) עם drag & drop. - Database: שדה folder ב-collection_items, מטאדאטה folders ב-user_collections - API: endpoints חדשים ל-CRUD תיקיות (create/rename/delete/reorder) - Frontend: רינדור accordion, מודאל יצירת תיקיה, inline rename, drag & drop - CSS: סגנונות לתיקיות מתקפלות עם RTL ו-responsive - Shared view: תצוגת תיקיות בצפייה ציבורית (read-only) https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: תיקון 3 באגים בתיקיות אוספים (Bugbot PR #3143) 1. Mixed mode dedup: מפתח dedup חזר ל-2-tuple (source, file_name) בלבד כדי למנוע כפילויות כשקובץ ידני בתיקיה ומחושב ב-root 2. Route conflict: הוספת ולידציה לשמות תיקיות שמורים ("reorder") כדי למנוע התנגשות עם endpoint סידור תיקיות 3. Drag reorder: איסוף פריטים מכל ה-sections (root + תיקיות) כדי לא לאבד פריטים מתיקיות אחרות בעת שליחת סדר לשרת https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: תיקון 3 באגים נוספים בתיקיות אוספים (Bugbot סבב 2) 1. Missing migration backfill: פריטים קיימים ללא folder מקבלים "" (root) כדי למנוע אי-התאמה בין null ל-"" באינדקס MongoDB 2. Cross-folder drag metadata: endpoint חדש move_item_folder שמעביר פריט בין תיקיות עם שמירת כל המטאדאטה (notes, tags, pinned) במקום add+remove שמאבד מידע 3. Root drop zone: container ה-root מרונדר תמיד (גם כשריק) כדי שתמיד יהיה אזור drop לגרירת פריטים חזרה ל-root https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: הסתרת פרטי exception מתגובת API (CodeQL) move_item_folder החזיר str(e) ישירות למשתמש — חשיפת מידע רגיש. מוחלף בהודעת שגיאה גנרית + לוג פנימי. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: תיקון 3 באגים נוספים בתיקיות אוספים (Bugbot סבב 3) 1. Empty state: כשאין פריטים ואין תיקיות, buildFolderGroupedHtml מחזיר ריק כדי שהודעת "אין פריטים" תוצג כמו לפני השינוי 2. Folder drop race: סימון dropInProgress בעת drop על תיקיה/root כדי שדragend לא ישלח reorder מיותר עם סדר ישן 3. Add-folder button: מוסתר ב-workspace collections כי תיקיות לא רלוונטיות שם https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: handle DuplicateKeyError in move_item_folder When moving an item to a folder where the same file already exists, the unique index throws DuplicateKeyError. Now catches it gracefully by removing the source item (target already has the file). https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: add missing folder field to workspace remove payload The workspace board's remove handler was missing the folder field in the removeItems payload, while the regular view handler included it. This could cause silent removal failures for items in non-root folders. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: merge metadata on duplicate move & allow same file in multiple folders 1. move_item_folder: on DuplicateKeyError, merge source metadata (note, tags, pinned, workspace_state) into target before deleting the source item, preventing silent metadata loss. 2. Mixed-mode dedup: manual items no longer deduplicated against each other across folders. The seen set now only filters computed items that duplicate manual ones. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: use 'is not None' for metadata merge in move_item_folder The truthiness check 'if val:' skipped merging pinned=False and empty tags/notes. Changed to 'is not None' to handle boolean and empty-string fields correctly. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: refresh counts after duplicate move & strip folder on cross-collection drag 1. move_item_folder: call _refresh_collection_counts after deleting source item on DuplicateKeyError so items_count stays accurate. 2. Cross-collection sidebar drag: strip folder from payload when adding item to a different collection, preventing orphaned folders in the target collection. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: move dropInProgress after validation & resilient rename fallback 1. Folder/root drop handlers: set dropInProgress only after validating drag data and checking same-folder no-op, preventing leaked drag state on cancelled drops. 2. rename_folder fallback loop: wrap each update_one in its own try/except so a single DuplicateKeyError doesn't abort remaining items. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: add DuplicateKeyError fallback stub & only merge meaningful metadata 1. Add DuplicateKeyError stub class in the pymongo-unavailable fallback block to prevent NameError in move_item_folder. 2. Metadata merge on duplicate move now only copies meaningful values (non-empty note, non-empty tags, pinned=True, non-empty workspace_state) to avoid overwriting destination metadata with source defaults. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: correct workspace_state type check from dict to str in merge workspace_state is a string (e.g. "todo", "in_progress", "done"), not a dict. The isinstance(dict) check was always False, silently dropping workspace_state during duplicate-key folder moves. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: prevent double HTML-escaping of folder names & cleanup drag on failure 1. create_folder/update_folder: replace sanitize_input (which calls html.escape) with plain strip+truncate. The DB layer validates folder names, and the frontend escapes on render — no need to HTML-encode before storage. 2. Folder drop handlers: wrap moveItemFolder calls in try/finally with clearActiveDragContext() to prevent leaked drag state when the API call fails. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: move orphaned items to root after folder rename failures After the rename fallback loop, any items still carrying the old folder name (due to DuplicateKeyError or other failures) are moved to root folder to prevent orphaned items pointing to a non-existent folder in the collection metadata. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: run backfill migration only once per process Added class-level _migration_done flag so the drop_index and update_many backfill operations only execute on first instantiation, not on every API request. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: only merge source metadata into target when target field is empty Fetch the destination item before merging so that existing metadata (note, tags, pinned, workspace_state) on the target is preserved. Source values are only copied when the target field is empty/missing. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: add folder data to touch handler reorder payload The touch endHandler in wireDnd was not updated with folder support. Now traverses up to #collectionItems, uses the correct selector for all sections, and includes folder field in the reorder payload. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: improve folder drag-and-drop reliability 1. Fix dragleave flicker on folder headers by checking relatedTarget (same pattern used by sidebar drop handlers). 2. Set dropEffect='move' on dragover for visual feedback. 3. Make entire card draggable (not just the handle) so users can drag from anywhere on the card to move it between folders. 4. Add stopPropagation on drop to prevent details/summary toggle. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: prevent duplicate folder event handlers & add dragend on card 1. Use AbortController in wireFolderEvents to remove all prior listeners before re-registering on each render, preventing duplicate click/drag handlers from stacking up. 2. Add dragend handler on the card element (not just the handle) so drag cleanup runs correctly when dragging from card body. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: prevent double-firing drag events from nested draggable elements Remove draggable="true" from the handle span and keep only on the article card. Remove handle-specific dragstart/dragend listeners, keeping only the card-level ones. This prevents event bubbling from causing duplicate API reorder calls. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: resolve IndexOptionsConflict in rules_storage index creation rules_storage.py was creating indexes without explicit names, which conflicted with the same indexes created by database/manager.py with name 'visual_rules_enabled_idx'. Now uses matching explicit names and downgrades code 85/86 conflicts to debug level. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * fix: wrap each create_index in its own try-except to avoid skipping remaining indexes A single try block meant an early index failure (e.g. name conflict) would silently prevent all subsequent indexes from being created. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G * feat: persist folder open/closed state across page navigations Uses localStorage to remember whether each folder is open or closed. When the user closes a folder and navigates away, it stays closed when they return. https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G --------- Co-authored-by: Claude <noreply@anthropic.com>

✨ תיאור קצר
הוספת תמיכה בארגון פריטים בתיקיות (folders) בתוך אוספים. משתמשים יכולים ליצור, לשנות שם, למחוק ותיקיות, ולהעביר פריטים בין תיקיות באמצעות drag & drop.
📦 שינויים עיקריים
פירוט:
Backend (database/collections_manager.py):
create_folder,rename_folder,delete_folder,reorder_foldersfolderלפריטים (items) עם ערך ברירת מחדל''(root)folderנוסף לאינדקס ייחודי של פריטיםunique_item(ללא folder) והחלפתו ב-unique_item_folderAPI (webapp/collections_api.py):
POST /api/collections/<id>/folders- יצירת תיקיהPUT /api/collections/<id>/folders/<name>- עדכון תיקיהDELETE /api/collections/<id>/folders/<name>- מחיקת תיקיהPUT /api/collections/<id>/folders/reorder- סידור תיקיותGET /api/collections/<id>/itemsעם תמיכה ב-folder_filterquery parameterFrontend (webapp/static/js/collections.js):
createFolder,updateFolder,deleteFolder,reorderFolders<details>)Shared Collections (webapp/static/js/collection_shared.js):
CSS (webapp/static/css/collections.css):
.collection-folder,.collection-folder__header, וכו'🧪 בדיקות
https://claude.ai/code/session_01EukqPajpmNN2jnAmB5xz9G