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.
This commit is contained in:
Percy
2026-08-07 15:25:33 +08:00
parent 37be4716ff
commit 5e12984f44
2 changed files with 28 additions and 8 deletions
+26 -6
View File
@@ -246,11 +246,31 @@ function HomePage() {
}
};
const { importing, fileInputRef, triggerFileSelect, handleFileChange } = useImportClassroom(
() => {
loadClassrooms();
},
);
// Capture the active folder when an import starts so the imported course
// lands in that folder, not whichever folder is active when the async import
// resolves (the user may have navigated away in the meantime).
const importFolderRef = useRef<string | undefined>(undefined);
const handleImportSuccess = async (importedStageId: string) => {
const folderId = importFolderRef.current;
importFolderRef.current = undefined;
// File the imported course into the folder that was active when the
// import began, before refreshing the list so the card appears in place.
if (folderId) {
try {
await setStageFolder(importedStageId, folderId);
} catch (err) {
log.error('Failed to assign imported course to folder:', err);
toast.error(t('classroom.moveFailed'));
}
}
await loadClassrooms();
};
const { importing, fileInputRef, triggerFileSelect, handleFileChange } =
useImportClassroom(handleImportSuccess);
const triggerImport = () => {
importFolderRef.current = currentFolderId;
triggerFileSelect();
};
const {
importing: pptxImporting,
@@ -1035,7 +1055,7 @@ function HomePage() {
</AnimatePresence>
<button
onClick={triggerFileSelect}
onClick={triggerImport}
disabled={importing}
className="group/import grid grid-cols-[auto_0fr] hover:grid-cols-[auto_1fr] items-center gap-1 rounded-full px-1.5 py-0.5 text-[12px] text-muted-foreground/35 hover:text-muted-foreground/70 hover:bg-muted/50 transition-all duration-200 cursor-pointer"
>
+2 -2
View File
@@ -26,7 +26,7 @@ export type ImportPhase =
| 'writingCourse'
| 'done';
export function useImportClassroom(onSuccess?: () => void) {
export function useImportClassroom(onSuccess?: (importedStageId: string) => void) {
const [importing, setImporting] = useState(false);
const [phase, setPhase] = useState<ImportPhase>('idle');
const fileInputRef = useRef<HTMLInputElement>(null);
@@ -236,7 +236,7 @@ export function useImportClassroom(onSuccess?: () => void) {
// 6. Done
setPhase('done');
toast.success(t('import.success'), { id: toastId });
onSuccess?.();
onSuccess?.(newStageId);
} catch (error) {
log.error('Classroom ZIP import failed:', error);
// Media files are a separate legacy domain and cannot join the document transaction.