| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
feat(courses): add folder grouping (#1005) * feat(courses): add folder grouping behind a feature flag Group courses into user-created folders on the home page. Folders are device-local organization metadata kept in the existing IndexedDB database (new folders + course-to-folder mapping tables, Dexie v16); the course document aggregate owned by the DocumentStore is untouched. - Create / rename (inline) / delete folders (keep or remove members) - Move a course between folders via a hover menu - Navigate into a folder with a breadcrumb back to all courses - Search flattens the list, annotating each course with its folder - Deleting a course cleans up its folder membership - All UI strings internationalized across 9 locales - Gated behind NEXT_PUBLIC_ENABLE_COURSE_FOLDERS (default OFF) * test(courses): update db mocks and version assertion for folder tables The folder feature adds the `stageFolders` table to the deletion cascade and bumps the Dexie schema to v16. Update the affected test db mocks to include the new table and bump the version assertion accordingly. * fix(courses): stop course click-through when selecting from the move menu The move-to-folder menu is rendered inside the course card's clickable container. Selecting a folder item could let the click event reach the card's onClick (opening the course) because only the trigger button stopped propagation. Stop propagation on pointerdown for the trigger and on click for every menu item so selecting a destination never opens the course. * fix(courses): open a dialog for the move-menu new-folder entry The move-to-folder menu's inline "new folder" input never worked: a Radix DropdownMenu is modal, so a raw <input> inside it cannot keep focus — Radix closes the menu the instant the input is focused, dropping the field before anything can be typed. Move the new-folder entry out of the menu: it now asks the caller to open the existing NewFolderDialog and, on confirm, moves the requesting course into the freshly created folder. * feat(courses): drag-to-file folders and stacked cover thumbnails Per review feedback, make folders feel complete: - Drag a course card onto a folder tile to file it there. The tile turns into a clear drop target (ring + overlay) while a drag is over it. The hover 📂 menu remains as the accessible fallback for keyboard/touch. - Replace the placeholder folder icon with a stable stack of up to three member course covers (most recently updated frontmost). Empty folders keep the folder icon; the name and course count are always visible. * feat(courses): refine folder cover stack to a tidy fanned layout Tune the folder-tile cover stack per review: front cover centered and upright, rear covers peek out from alternating sides with a slight tilt and reduced opacity, soft shadow and hairline ring. Reads as a neat pile of course covers rather than an exaggerated fan. * fix(courses): address review — dialog mount, empty-list, rename validation, partial-delete refresh, breadcrumb dedup Per review (wyuc, CHANGES_REQUESTED): 1. Mount NewFolderDialog/DeleteFolderDialog outside the collapsible Recent subtree so they are reachable while it is collapsed; the New-folder button now expands the section before opening the dialog. 2. Keep the Recent/folder surface alive when the course list is empty so folders remain reachable and a first folder can be created. 3. Enforce folder-name validation (width + uniqueness) on rename as well as create, at the storage boundary (FolderNameError) and in the UI. 4. Refresh authoritative state on folder-delete both success and failure, so a partial "remove" failure does not leave stale cards/counts. 5. Drop the duplicated folder breadcrumb from the top section header; keep a single navigation breadcrumb in the content area. Add focused storage tests covering rename validation, membership writes, both deletion modes, and partial-failure propagation. * fix(courses): address 2nd review — folder-view layout, drop highlight, validation parity, a11y, i18n Per review (wyuc, CHANGES_REQUESTED on 9f77686f): P1 — folder view layout: opening a folder compacts the hero and surfaces the library content; the centered header shows a single path "Recent > Folder name"; the empty-folder state sits directly below it; clicking Recent returns to the root grid. The duplicate content breadcrumb is dropped (kept only for search). P2 — correctness & a11y: - Clear the folder drop highlight on every drag exit (dragenter counter) and gate it on the text/stage-id payload. - Duplicate-name check in the dialog is now case-insensitive, matching the storage boundary; FolderNameError is mapped to specific messages. - Renames report the actual submitted width, not a hardcoded 0. - createFolder/renameFolder run read-check-write in one read-write transaction so the uniqueness invariant cannot race across tabs. - The move-menu trigger is focus-visible and pointer-coarse reachable (visible on touch / keyboard), not hover-only. Extra: - Empty library (no courses, no folders) shows a dedicated hint instead of the search-empty string. - Translate all newly added folder strings across ja/ko/pt/ru/ar, fix Simplified-Chinese text in zh-TW, and add the emptyLibraryHint key. * fix(courses): address 3rd review — ungate folders, fix empty-library hero, stabilize the bar Per review (wyuc, CHANGES_REQUESTED on 19729038): P1 — ship folders unconditionally: remove NEXT_PUBLIC_ENABLE_COURSE_FOLDERS, the isCourseFoldersEnabled helper, the .env.example entry, and every flag-on/flag-off UI branch. Folder metadata is always loaded; create, move, drag, and folder navigation are always available. The IndexedDB v16 schema stays intact. P1 — full-screen landing hero only when the library is truly empty: the hero uses min-h-[calc(100dvh-8rem)] only when there are zero courses AND zero folders. A `hydrated` flag waits for both async loads before selecting the layout, so folders arriving from storage do not flip the hero from full-screen to compact. P2 — geometrically stable centered bar: the Recent bar gets a fixed height (h-9) so entering/leaving a folder (which toggles the New-folder action and the folder path) does not shift the search/import controls. * fix(courses): single stable library action bar across root, folder, and empty states Per review (wyuc, CHANGES_REQUESTED on 860a1170): P2 — remove the duplicate floating import controls. The hero section rendered a second Import Classroom / PPTX cluster whenever the course list was empty, duplicating the Recent bar's actions and floating above it as the hero switched layout modes. Import now lives only in the Recent action bar. P1 — keep folder creation reachable for a truly empty library. The Recent section is now always rendered after hydration (not gated on having courses or folders), so a new user with zero of each can still create the first folder or import. The empty-library hint renders below the single stable action bar. Invariant: one library action bar across root, folder, and empty states; state changes alter the path and enabled actions only. * chore: trigger CI after ready-for-review * fix(courses): inherit folder context when importing from inside a folder Per review (wyuc, CHANGES_REQUESTED on 37be4716): [P1] Courses imported from inside a folder were silently placed at the root. The import contract now carries the new stageId to the success callback; the page captures the active folder when the file picker opens (not when the async import resolves) and files the imported course into that folder before the list refresh, so the card appears immediately and the folder count increments. Root imports remain ungrouped. A failed folder assignment surfaces an explicit error toast instead of silently falling back. * fix(courses): stable hero on folder delete, lightweight delete menu, breadcrumb count, empty-name validation Per review (wyuc, CHANGES_REQUESTED on 5e12984f) + QA findings: P1 — deleting the last folder must not expand the hero. The full-screen landing hero is a first-visit treatment only: a session-scoped "librarySeen" flag latches true once the library bar renders, so the hero stays compact across all subsequent create/delete transitions. P2 — replace the heavy two-card delete dialog with progressive disclosure. Empty folder: an inline confirmation overlay on the card tile (matching the course-delete pattern), with the empty-folder copy. Non-empty folder: a compact dropdown beside the delete icon — "Delete folder only" (courses move to unfiled) executes directly; "Delete folder and N courses" opens a lightweight destructive confirmation. The full modal is gone. QA fixes: - Breadcrumb count is now contextual (total at root, in-folder count inside a folder) instead of always showing the global total. - Renaming a folder to empty/whitespace shows a "name cannot be empty" error and shakes, instead of silently exiting edit mode. * fix(courses): cross-review cleanup — dead i18n keys, missing translations, delete-overlay reset Post-commit cross-review (leak audit PASS, wyuc 19/19 PASS) found: - Remove 6 dead i18n keys left over from the removed two-card delete dialog (deleteFolderDesc, deleteFolderUngroupTitle, etc.) across all 9 locales. - Translate all remaining English folder strings in ja/ko/pt/ru/ar (newFolderTitle, folderNameLabel, folderCreate, deleteFolderTitle, etc.). - Fix zh-TW: convert ~17 simplified-Chinese folder strings to Traditional (新增資料夾/建立/刪除/etc.). - Delete the orphaned "feature flag" comment (flag was removed earlier). - Close the inline delete-confirm overlay before the async delete, so a failure leaves the card interactive instead of stuck behind the backdrop. * fix(courses): clear drop highlight on drag end, map limit error in dialog Two minor findings from cross-review: - Escape-cancelled drags may not fire dragleave on every folder target, leaving a highlight ring. The course card now dispatches a 'course-drag-end' window event on dragEnd (fires for both normal drop and Escape cancel); folder cards listen and clear their drop state. - FolderNameError kind 'limit' (thrown at the storage boundary when FOLDER_COUNT_LIMIT is reached in a cross-tab race) now maps to the specific folderCountLimit message in the dialog instead of falling through to a generic hint. Also fixes an SSR hydration mismatch: librarySeen is now initialized to false and read from sessionStorage in useEffect (not in the useState initializer). * fix(courses): atomic folder removal, always-compact hero, no-cover placeholder Per review (wyuc, CHANGES_REQUESTED on 470f5fea): P2 — close the orphan-membership race in 'remove' mode. deleteFolder now captures members, deletes the folder row, and clears all memberships in ONE transaction BEFORE the course-deletion cascade. The folder is gone from the moment the cascade starts, so a concurrent setStageFolder (which checks existence in its own transaction) rejects the assignment. P2 — remove the first-visit full-screen hero. The librarySeen flag caused a visible layout jump on refresh (SSR renders full-screen, then the effect reads sessionStorage and switches to compact). The hero is now always compact (mt-[10vh]); no sessionStorage, no hydration mismatch, no geometry regression. P3 — distinct no-cover fallback for non-empty folders. A folder with courses but no cached thumbnails now shows a neutral stacked-card placeholder instead of the empty-folder icon. Merged with latest main; no conflicts. --------- Co-authored-by: Percy <percy@PercydeMacBook-Pro.local> | 29 天前 | |
feat(courses): add folder grouping (#1005) * feat(courses): add folder grouping behind a feature flag Group courses into user-created folders on the home page. Folders are device-local organization metadata kept in the existing IndexedDB database (new folders + course-to-folder mapping tables, Dexie v16); the course document aggregate owned by the DocumentStore is untouched. - Create / rename (inline) / delete folders (keep or remove members) - Move a course between folders via a hover menu - Navigate into a folder with a breadcrumb back to all courses - Search flattens the list, annotating each course with its folder - Deleting a course cleans up its folder membership - All UI strings internationalized across 9 locales - Gated behind NEXT_PUBLIC_ENABLE_COURSE_FOLDERS (default OFF) * test(courses): update db mocks and version assertion for folder tables The folder feature adds the `stageFolders` table to the deletion cascade and bumps the Dexie schema to v16. Update the affected test db mocks to include the new table and bump the version assertion accordingly. * fix(courses): stop course click-through when selecting from the move menu The move-to-folder menu is rendered inside the course card's clickable container. Selecting a folder item could let the click event reach the card's onClick (opening the course) because only the trigger button stopped propagation. Stop propagation on pointerdown for the trigger and on click for every menu item so selecting a destination never opens the course. * fix(courses): open a dialog for the move-menu new-folder entry The move-to-folder menu's inline "new folder" input never worked: a Radix DropdownMenu is modal, so a raw <input> inside it cannot keep focus — Radix closes the menu the instant the input is focused, dropping the field before anything can be typed. Move the new-folder entry out of the menu: it now asks the caller to open the existing NewFolderDialog and, on confirm, moves the requesting course into the freshly created folder. * feat(courses): drag-to-file folders and stacked cover thumbnails Per review feedback, make folders feel complete: - Drag a course card onto a folder tile to file it there. The tile turns into a clear drop target (ring + overlay) while a drag is over it. The hover 📂 menu remains as the accessible fallback for keyboard/touch. - Replace the placeholder folder icon with a stable stack of up to three member course covers (most recently updated frontmost). Empty folders keep the folder icon; the name and course count are always visible. * feat(courses): refine folder cover stack to a tidy fanned layout Tune the folder-tile cover stack per review: front cover centered and upright, rear covers peek out from alternating sides with a slight tilt and reduced opacity, soft shadow and hairline ring. Reads as a neat pile of course covers rather than an exaggerated fan. * fix(courses): address review — dialog mount, empty-list, rename validation, partial-delete refresh, breadcrumb dedup Per review (wyuc, CHANGES_REQUESTED): 1. Mount NewFolderDialog/DeleteFolderDialog outside the collapsible Recent subtree so they are reachable while it is collapsed; the New-folder button now expands the section before opening the dialog. 2. Keep the Recent/folder surface alive when the course list is empty so folders remain reachable and a first folder can be created. 3. Enforce folder-name validation (width + uniqueness) on rename as well as create, at the storage boundary (FolderNameError) and in the UI. 4. Refresh authoritative state on folder-delete both success and failure, so a partial "remove" failure does not leave stale cards/counts. 5. Drop the duplicated folder breadcrumb from the top section header; keep a single navigation breadcrumb in the content area. Add focused storage tests covering rename validation, membership writes, both deletion modes, and partial-failure propagation. * fix(courses): address 2nd review — folder-view layout, drop highlight, validation parity, a11y, i18n Per review (wyuc, CHANGES_REQUESTED on 9f77686f): P1 — folder view layout: opening a folder compacts the hero and surfaces the library content; the centered header shows a single path "Recent > Folder name"; the empty-folder state sits directly below it; clicking Recent returns to the root grid. The duplicate content breadcrumb is dropped (kept only for search). P2 — correctness & a11y: - Clear the folder drop highlight on every drag exit (dragenter counter) and gate it on the text/stage-id payload. - Duplicate-name check in the dialog is now case-insensitive, matching the storage boundary; FolderNameError is mapped to specific messages. - Renames report the actual submitted width, not a hardcoded 0. - createFolder/renameFolder run read-check-write in one read-write transaction so the uniqueness invariant cannot race across tabs. - The move-menu trigger is focus-visible and pointer-coarse reachable (visible on touch / keyboard), not hover-only. Extra: - Empty library (no courses, no folders) shows a dedicated hint instead of the search-empty string. - Translate all newly added folder strings across ja/ko/pt/ru/ar, fix Simplified-Chinese text in zh-TW, and add the emptyLibraryHint key. * fix(courses): address 3rd review — ungate folders, fix empty-library hero, stabilize the bar Per review (wyuc, CHANGES_REQUESTED on 19729038): P1 — ship folders unconditionally: remove NEXT_PUBLIC_ENABLE_COURSE_FOLDERS, the isCourseFoldersEnabled helper, the .env.example entry, and every flag-on/flag-off UI branch. Folder metadata is always loaded; create, move, drag, and folder navigation are always available. The IndexedDB v16 schema stays intact. P1 — full-screen landing hero only when the library is truly empty: the hero uses min-h-[calc(100dvh-8rem)] only when there are zero courses AND zero folders. A `hydrated` flag waits for both async loads before selecting the layout, so folders arriving from storage do not flip the hero from full-screen to compact. P2 — geometrically stable centered bar: the Recent bar gets a fixed height (h-9) so entering/leaving a folder (which toggles the New-folder action and the folder path) does not shift the search/import controls. * fix(courses): single stable library action bar across root, folder, and empty states Per review (wyuc, CHANGES_REQUESTED on 860a1170): P2 — remove the duplicate floating import controls. The hero section rendered a second Import Classroom / PPTX cluster whenever the course list was empty, duplicating the Recent bar's actions and floating above it as the hero switched layout modes. Import now lives only in the Recent action bar. P1 — keep folder creation reachable for a truly empty library. The Recent section is now always rendered after hydration (not gated on having courses or folders), so a new user with zero of each can still create the first folder or import. The empty-library hint renders below the single stable action bar. Invariant: one library action bar across root, folder, and empty states; state changes alter the path and enabled actions only. * chore: trigger CI after ready-for-review * fix(courses): inherit folder context when importing from inside a folder Per review (wyuc, CHANGES_REQUESTED on 37be4716): [P1] Courses imported from inside a folder were silently placed at the root. The import contract now carries the new stageId to the success callback; the page captures the active folder when the file picker opens (not when the async import resolves) and files the imported course into that folder before the list refresh, so the card appears immediately and the folder count increments. Root imports remain ungrouped. A failed folder assignment surfaces an explicit error toast instead of silently falling back. * fix(courses): stable hero on folder delete, lightweight delete menu, breadcrumb count, empty-name validation Per review (wyuc, CHANGES_REQUESTED on 5e12984f) + QA findings: P1 — deleting the last folder must not expand the hero. The full-screen landing hero is a first-visit treatment only: a session-scoped "librarySeen" flag latches true once the library bar renders, so the hero stays compact across all subsequent create/delete transitions. P2 — replace the heavy two-card delete dialog with progressive disclosure. Empty folder: an inline confirmation overlay on the card tile (matching the course-delete pattern), with the empty-folder copy. Non-empty folder: a compact dropdown beside the delete icon — "Delete folder only" (courses move to unfiled) executes directly; "Delete folder and N courses" opens a lightweight destructive confirmation. The full modal is gone. QA fixes: - Breadcrumb count is now contextual (total at root, in-folder count inside a folder) instead of always showing the global total. - Renaming a folder to empty/whitespace shows a "name cannot be empty" error and shakes, instead of silently exiting edit mode. * fix(courses): cross-review cleanup — dead i18n keys, missing translations, delete-overlay reset Post-commit cross-review (leak audit PASS, wyuc 19/19 PASS) found: - Remove 6 dead i18n keys left over from the removed two-card delete dialog (deleteFolderDesc, deleteFolderUngroupTitle, etc.) across all 9 locales. - Translate all remaining English folder strings in ja/ko/pt/ru/ar (newFolderTitle, folderNameLabel, folderCreate, deleteFolderTitle, etc.). - Fix zh-TW: convert ~17 simplified-Chinese folder strings to Traditional (新增資料夾/建立/刪除/etc.). - Delete the orphaned "feature flag" comment (flag was removed earlier). - Close the inline delete-confirm overlay before the async delete, so a failure leaves the card interactive instead of stuck behind the backdrop. * fix(courses): clear drop highlight on drag end, map limit error in dialog Two minor findings from cross-review: - Escape-cancelled drags may not fire dragleave on every folder target, leaving a highlight ring. The course card now dispatches a 'course-drag-end' window event on dragEnd (fires for both normal drop and Escape cancel); folder cards listen and clear their drop state. - FolderNameError kind 'limit' (thrown at the storage boundary when FOLDER_COUNT_LIMIT is reached in a cross-tab race) now maps to the specific folderCountLimit message in the dialog instead of falling through to a generic hint. Also fixes an SSR hydration mismatch: librarySeen is now initialized to false and read from sessionStorage in useEffect (not in the useState initializer). * fix(courses): atomic folder removal, always-compact hero, no-cover placeholder Per review (wyuc, CHANGES_REQUESTED on 470f5fea): P2 — close the orphan-membership race in 'remove' mode. deleteFolder now captures members, deletes the folder row, and clears all memberships in ONE transaction BEFORE the course-deletion cascade. The folder is gone from the moment the cascade starts, so a concurrent setStageFolder (which checks existence in its own transaction) rejects the assignment. P2 — remove the first-visit full-screen hero. The librarySeen flag caused a visible layout jump on refresh (SSR renders full-screen, then the effect reads sessionStorage and switches to compact). The hero is now always compact (mt-[10vh]); no sessionStorage, no hydration mismatch, no geometry regression. P3 — distinct no-cover fallback for non-empty folders. A folder with courses but no cached thumbnails now shows a neutral stacked-card placeholder instead of the empty-folder icon. Merged with latest main; no conflicts. --------- Co-authored-by: Percy <percy@PercydeMacBook-Pro.local> | 29 天前 | |
feat(courses): add folder grouping (#1005) * feat(courses): add folder grouping behind a feature flag Group courses into user-created folders on the home page. Folders are device-local organization metadata kept in the existing IndexedDB database (new folders + course-to-folder mapping tables, Dexie v16); the course document aggregate owned by the DocumentStore is untouched. - Create / rename (inline) / delete folders (keep or remove members) - Move a course between folders via a hover menu - Navigate into a folder with a breadcrumb back to all courses - Search flattens the list, annotating each course with its folder - Deleting a course cleans up its folder membership - All UI strings internationalized across 9 locales - Gated behind NEXT_PUBLIC_ENABLE_COURSE_FOLDERS (default OFF) * test(courses): update db mocks and version assertion for folder tables The folder feature adds the `stageFolders` table to the deletion cascade and bumps the Dexie schema to v16. Update the affected test db mocks to include the new table and bump the version assertion accordingly. * fix(courses): stop course click-through when selecting from the move menu The move-to-folder menu is rendered inside the course card's clickable container. Selecting a folder item could let the click event reach the card's onClick (opening the course) because only the trigger button stopped propagation. Stop propagation on pointerdown for the trigger and on click for every menu item so selecting a destination never opens the course. * fix(courses): open a dialog for the move-menu new-folder entry The move-to-folder menu's inline "new folder" input never worked: a Radix DropdownMenu is modal, so a raw <input> inside it cannot keep focus — Radix closes the menu the instant the input is focused, dropping the field before anything can be typed. Move the new-folder entry out of the menu: it now asks the caller to open the existing NewFolderDialog and, on confirm, moves the requesting course into the freshly created folder. * feat(courses): drag-to-file folders and stacked cover thumbnails Per review feedback, make folders feel complete: - Drag a course card onto a folder tile to file it there. The tile turns into a clear drop target (ring + overlay) while a drag is over it. The hover 📂 menu remains as the accessible fallback for keyboard/touch. - Replace the placeholder folder icon with a stable stack of up to three member course covers (most recently updated frontmost). Empty folders keep the folder icon; the name and course count are always visible. * feat(courses): refine folder cover stack to a tidy fanned layout Tune the folder-tile cover stack per review: front cover centered and upright, rear covers peek out from alternating sides with a slight tilt and reduced opacity, soft shadow and hairline ring. Reads as a neat pile of course covers rather than an exaggerated fan. * fix(courses): address review — dialog mount, empty-list, rename validation, partial-delete refresh, breadcrumb dedup Per review (wyuc, CHANGES_REQUESTED): 1. Mount NewFolderDialog/DeleteFolderDialog outside the collapsible Recent subtree so they are reachable while it is collapsed; the New-folder button now expands the section before opening the dialog. 2. Keep the Recent/folder surface alive when the course list is empty so folders remain reachable and a first folder can be created. 3. Enforce folder-name validation (width + uniqueness) on rename as well as create, at the storage boundary (FolderNameError) and in the UI. 4. Refresh authoritative state on folder-delete both success and failure, so a partial "remove" failure does not leave stale cards/counts. 5. Drop the duplicated folder breadcrumb from the top section header; keep a single navigation breadcrumb in the content area. Add focused storage tests covering rename validation, membership writes, both deletion modes, and partial-failure propagation. * fix(courses): address 2nd review — folder-view layout, drop highlight, validation parity, a11y, i18n Per review (wyuc, CHANGES_REQUESTED on 9f77686f): P1 — folder view layout: opening a folder compacts the hero and surfaces the library content; the centered header shows a single path "Recent > Folder name"; the empty-folder state sits directly below it; clicking Recent returns to the root grid. The duplicate content breadcrumb is dropped (kept only for search). P2 — correctness & a11y: - Clear the folder drop highlight on every drag exit (dragenter counter) and gate it on the text/stage-id payload. - Duplicate-name check in the dialog is now case-insensitive, matching the storage boundary; FolderNameError is mapped to specific messages. - Renames report the actual submitted width, not a hardcoded 0. - createFolder/renameFolder run read-check-write in one read-write transaction so the uniqueness invariant cannot race across tabs. - The move-menu trigger is focus-visible and pointer-coarse reachable (visible on touch / keyboard), not hover-only. Extra: - Empty library (no courses, no folders) shows a dedicated hint instead of the search-empty string. - Translate all newly added folder strings across ja/ko/pt/ru/ar, fix Simplified-Chinese text in zh-TW, and add the emptyLibraryHint key. * fix(courses): address 3rd review — ungate folders, fix empty-library hero, stabilize the bar Per review (wyuc, CHANGES_REQUESTED on 19729038): P1 — ship folders unconditionally: remove NEXT_PUBLIC_ENABLE_COURSE_FOLDERS, the isCourseFoldersEnabled helper, the .env.example entry, and every flag-on/flag-off UI branch. Folder metadata is always loaded; create, move, drag, and folder navigation are always available. The IndexedDB v16 schema stays intact. P1 — full-screen landing hero only when the library is truly empty: the hero uses min-h-[calc(100dvh-8rem)] only when there are zero courses AND zero folders. A `hydrated` flag waits for both async loads before selecting the layout, so folders arriving from storage do not flip the hero from full-screen to compact. P2 — geometrically stable centered bar: the Recent bar gets a fixed height (h-9) so entering/leaving a folder (which toggles the New-folder action and the folder path) does not shift the search/import controls. * fix(courses): single stable library action bar across root, folder, and empty states Per review (wyuc, CHANGES_REQUESTED on 860a1170): P2 — remove the duplicate floating import controls. The hero section rendered a second Import Classroom / PPTX cluster whenever the course list was empty, duplicating the Recent bar's actions and floating above it as the hero switched layout modes. Import now lives only in the Recent action bar. P1 — keep folder creation reachable for a truly empty library. The Recent section is now always rendered after hydration (not gated on having courses or folders), so a new user with zero of each can still create the first folder or import. The empty-library hint renders below the single stable action bar. Invariant: one library action bar across root, folder, and empty states; state changes alter the path and enabled actions only. * chore: trigger CI after ready-for-review * fix(courses): inherit folder context when importing from inside a folder Per review (wyuc, CHANGES_REQUESTED on 37be4716): [P1] Courses imported from inside a folder were silently placed at the root. The import contract now carries the new stageId to the success callback; the page captures the active folder when the file picker opens (not when the async import resolves) and files the imported course into that folder before the list refresh, so the card appears immediately and the folder count increments. Root imports remain ungrouped. A failed folder assignment surfaces an explicit error toast instead of silently falling back. * fix(courses): stable hero on folder delete, lightweight delete menu, breadcrumb count, empty-name validation Per review (wyuc, CHANGES_REQUESTED on 5e12984f) + QA findings: P1 — deleting the last folder must not expand the hero. The full-screen landing hero is a first-visit treatment only: a session-scoped "librarySeen" flag latches true once the library bar renders, so the hero stays compact across all subsequent create/delete transitions. P2 — replace the heavy two-card delete dialog with progressive disclosure. Empty folder: an inline confirmation overlay on the card tile (matching the course-delete pattern), with the empty-folder copy. Non-empty folder: a compact dropdown beside the delete icon — "Delete folder only" (courses move to unfiled) executes directly; "Delete folder and N courses" opens a lightweight destructive confirmation. The full modal is gone. QA fixes: - Breadcrumb count is now contextual (total at root, in-folder count inside a folder) instead of always showing the global total. - Renaming a folder to empty/whitespace shows a "name cannot be empty" error and shakes, instead of silently exiting edit mode. * fix(courses): cross-review cleanup — dead i18n keys, missing translations, delete-overlay reset Post-commit cross-review (leak audit PASS, wyuc 19/19 PASS) found: - Remove 6 dead i18n keys left over from the removed two-card delete dialog (deleteFolderDesc, deleteFolderUngroupTitle, etc.) across all 9 locales. - Translate all remaining English folder strings in ja/ko/pt/ru/ar (newFolderTitle, folderNameLabel, folderCreate, deleteFolderTitle, etc.). - Fix zh-TW: convert ~17 simplified-Chinese folder strings to Traditional (新增資料夾/建立/刪除/etc.). - Delete the orphaned "feature flag" comment (flag was removed earlier). - Close the inline delete-confirm overlay before the async delete, so a failure leaves the card interactive instead of stuck behind the backdrop. * fix(courses): clear drop highlight on drag end, map limit error in dialog Two minor findings from cross-review: - Escape-cancelled drags may not fire dragleave on every folder target, leaving a highlight ring. The course card now dispatches a 'course-drag-end' window event on dragEnd (fires for both normal drop and Escape cancel); folder cards listen and clear their drop state. - FolderNameError kind 'limit' (thrown at the storage boundary when FOLDER_COUNT_LIMIT is reached in a cross-tab race) now maps to the specific folderCountLimit message in the dialog instead of falling through to a generic hint. Also fixes an SSR hydration mismatch: librarySeen is now initialized to false and read from sessionStorage in useEffect (not in the useState initializer). * fix(courses): atomic folder removal, always-compact hero, no-cover placeholder Per review (wyuc, CHANGES_REQUESTED on 470f5fea): P2 — close the orphan-membership race in 'remove' mode. deleteFolder now captures members, deletes the folder row, and clears all memberships in ONE transaction BEFORE the course-deletion cascade. The folder is gone from the moment the cascade starts, so a concurrent setStageFolder (which checks existence in its own transaction) rejects the assignment. P2 — remove the first-visit full-screen hero. The librarySeen flag caused a visible layout jump on refresh (SSR renders full-screen, then the effect reads sessionStorage and switches to compact). The hero is now always compact (mt-[10vh]); no sessionStorage, no hydration mismatch, no geometry regression. P3 — distinct no-cover fallback for non-empty folders. A folder with courses but no cached thumbnails now shows a neutral stacked-card placeholder instead of the empty-folder icon. Merged with latest main; no conflicts. --------- Co-authored-by: Percy <percy@PercydeMacBook-Pro.local> | 29 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 29 天前 | ||
| 29 天前 | ||
| 29 天前 |