@package-uploader/ui 1.1.4 → 1.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.html CHANGED
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Package Uploader</title>
8
- <script type="module" crossorigin src="/assets/index-CAZIUAbb.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-BS61yFPt.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-DHoRoGws.css">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@package-uploader/ui",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "React UI for uploading and browsing courses on LMS platforms",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/api/client.ts CHANGED
@@ -294,10 +294,12 @@ export const api = {
294
294
  getLaunchUrl: (documentId: number) =>
295
295
  request<{ url: string }>('GET', `/documents/${documentId}/launch`),
296
296
 
297
- // Preview: extract SCORM ZIP to temp dir with bridge script for live preview
298
- startPreview: async (file: File): Promise<{ token: string; url: string }> => {
297
+ // Preview: patch + extract SCORM ZIP to temp dir with bridge script for live preview
298
+ startPreview: async (file: File, options?: { skin?: string; classMappings?: CourseClassMappings }): Promise<{ token: string; url: string }> => {
299
299
  const formData = new FormData();
300
300
  formData.append('file', file);
301
+ if (options?.skin) formData.append('skin', options.skin);
302
+ if (options?.classMappings) formData.append('classMappings', JSON.stringify(options.classMappings));
301
303
  return request<{ token: string; url: string }>('POST', '/preview', formData);
302
304
  },
303
305
 
@@ -135,6 +135,12 @@ export default function UploadModal({
135
135
  return;
136
136
  }
137
137
 
138
+ // Already parsed but no preview — toggle panel off (clear structure to collapse)
139
+ if (courseStructure) {
140
+ setCourseStructure(null);
141
+ return;
142
+ }
143
+
138
144
  // Already failed — don't retry
139
145
  if (parseFailed) return;
140
146
 
@@ -144,30 +150,34 @@ export default function UploadModal({
144
150
  setParsingStructure(true);
145
151
  setPreviewLoading(true);
146
152
 
147
- try {
148
- // Parse structure and start preview in parallel
149
- const [structure, preview] = await Promise.all([
150
- courseStructure
151
- ? Promise.resolve(courseStructure)
152
- : api.parseCourseStructure(files[0].file),
153
- api.startPreview(files[0].file),
154
- ]);
155
-
156
- if (structure) {
157
- setCourseStructure(structure);
158
- setParseFailed(false);
159
- setPreviewToken(preview.token);
160
- setPreviewUrl(preview.url);
161
- } else {
162
- setParseFailed(true);
163
- // Cleanup the preview if structure parse failed
164
- api.stopPreview(preview.token).catch(() => {});
165
- }
166
- } catch {
153
+ // Run parse + preview in parallel — preview shows immediately with loading spinner
154
+ const previewOptions: { skin?: string; classMappings?: CourseClassMappings } = {};
155
+ if (skinName.trim()) previewOptions.skin = skinName.trim();
156
+ if (Object.keys(classMappings).length > 0) previewOptions.classMappings = classMappings;
157
+
158
+ const [structure, preview] = await Promise.all([
159
+ api.parseCourseStructure(files[0].file).catch(() => null),
160
+ api.startPreview(files[0].file, previewOptions).catch(() => null),
161
+ ]);
162
+
163
+ setParsingStructure(false);
164
+
165
+ if (!structure) {
167
166
  setParseFailed(true);
167
+ setPreviewLoading(false);
168
+ // Clean up preview if it succeeded but parse didn't
169
+ if (preview?.token) api.stopPreview(preview.token).catch(() => {});
170
+ return;
171
+ }
172
+
173
+ setCourseStructure(structure);
174
+ setParseFailed(false);
175
+
176
+ if (preview) {
177
+ setPreviewToken(preview.token);
178
+ setPreviewUrl(preview.url);
168
179
  }
169
180
 
170
- setParsingStructure(false);
171
181
  setPreviewLoading(false);
172
182
  }
173
183
 
@@ -334,7 +344,9 @@ export default function UploadModal({
334
344
  ? 'Not a Rise course'
335
345
  : isPreviewMode
336
346
  ? 'Close Preview'
337
- : 'Customize Classes';
347
+ : courseStructure
348
+ ? `Customize Course (${courseStructure.lessons.length} lessons)`
349
+ : 'Customize Course';
338
350
 
339
351
  // Build the preview URL — needs the API base path prepended
340
352
  const fullPreviewUrl = previewUrl
@@ -542,20 +554,35 @@ export default function UploadModal({
542
554
  </div>
543
555
  )}
544
556
 
545
- {/* Customize Classes button */}
557
+ {/* Customize Classes button + panel */}
546
558
  {isSingleFile && !uploading && (
547
559
  <div className="structure-section">
548
560
  <button
549
- className={`structure-toggle-btn${parseFailed ? ' disabled' : ''}`}
561
+ className={`structure-toggle-btn${courseStructure && !isPreviewMode ? ' open' : ''}${parseFailed ? ' disabled' : ''}`}
550
562
  onClick={handleCustomizeClick}
551
563
  disabled={parsingStructure || parseFailed}
552
564
  >
553
565
  {parsingStructure && <div className="spinner" />}
554
566
  <span>{customizeLabel}</span>
555
- {assignmentCount > 0 && (
567
+ {!parsingStructure && !parseFailed && courseStructure && (
568
+ <span className="structure-toggle-arrow">
569
+ {courseStructure && !isPreviewMode ? '\u25B2' : '\u25BC'}
570
+ </span>
571
+ )}
572
+ {assignmentCount > 0 && !courseStructure && (
556
573
  <span className="course-structure-badge">{assignmentCount} assigned</span>
557
574
  )}
558
575
  </button>
576
+
577
+ {courseStructure && !isPreviewMode && (
578
+ <div className="structure-panel">
579
+ <CourseStructureStep
580
+ structure={courseStructure}
581
+ classMappings={classMappings}
582
+ onChange={setClassMappings}
583
+ />
584
+ </div>
585
+ )}
559
586
  </div>
560
587
  )}
561
588