@package-uploader/ui 1.1.4 → 1.1.5

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-BtMbH61W.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.5",
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,40 @@ 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(() => {});
153
+ // Parse structure first — this determines if we can proceed
154
+ let structure = courseStructure;
155
+ if (!structure) {
156
+ try {
157
+ structure = await api.parseCourseStructure(files[0].file);
158
+ } catch {
159
+ structure = null;
165
160
  }
166
- } catch {
161
+ }
162
+
163
+ if (!structure) {
167
164
  setParseFailed(true);
165
+ setParsingStructure(false);
166
+ setPreviewLoading(false);
167
+ return;
168
168
  }
169
169
 
170
+ setCourseStructure(structure);
171
+ setParseFailed(false);
170
172
  setParsingStructure(false);
173
+
174
+ // Start preview independently — patches with current skin + classMappings
175
+ try {
176
+ const previewOptions: { skin?: string; classMappings?: CourseClassMappings } = {};
177
+ if (skinName.trim()) previewOptions.skin = skinName.trim();
178
+ if (Object.keys(classMappings).length > 0) previewOptions.classMappings = classMappings;
179
+ const preview = await api.startPreview(files[0].file, previewOptions);
180
+ setPreviewToken(preview.token);
181
+ setPreviewUrl(preview.url);
182
+ } catch (err) {
183
+ console.warn('Preview not available:', err);
184
+ // Preview failed but structure is valid — stay in non-preview mode
185
+ }
186
+
171
187
  setPreviewLoading(false);
172
188
  }
173
189
 
@@ -334,6 +350,8 @@ export default function UploadModal({
334
350
  ? 'Not a Rise course'
335
351
  : isPreviewMode
336
352
  ? 'Close Preview'
353
+ : courseStructure
354
+ ? `Customize Classes (${courseStructure.lessons.length} lessons)`
337
355
  : 'Customize Classes';
338
356
 
339
357
  // Build the preview URL — needs the API base path prepended
@@ -542,20 +560,35 @@ export default function UploadModal({
542
560
  </div>
543
561
  )}
544
562
 
545
- {/* Customize Classes button */}
563
+ {/* Customize Classes button + panel */}
546
564
  {isSingleFile && !uploading && (
547
565
  <div className="structure-section">
548
566
  <button
549
- className={`structure-toggle-btn${parseFailed ? ' disabled' : ''}`}
567
+ className={`structure-toggle-btn${courseStructure && !isPreviewMode ? ' open' : ''}${parseFailed ? ' disabled' : ''}`}
550
568
  onClick={handleCustomizeClick}
551
569
  disabled={parsingStructure || parseFailed}
552
570
  >
553
571
  {parsingStructure && <div className="spinner" />}
554
572
  <span>{customizeLabel}</span>
555
- {assignmentCount > 0 && (
573
+ {!parsingStructure && !parseFailed && courseStructure && (
574
+ <span className="structure-toggle-arrow">
575
+ {courseStructure && !isPreviewMode ? '\u25B2' : '\u25BC'}
576
+ </span>
577
+ )}
578
+ {assignmentCount > 0 && !courseStructure && (
556
579
  <span className="course-structure-badge">{assignmentCount} assigned</span>
557
580
  )}
558
581
  </button>
582
+
583
+ {courseStructure && !isPreviewMode && (
584
+ <div className="structure-panel">
585
+ <CourseStructureStep
586
+ structure={courseStructure}
587
+ classMappings={classMappings}
588
+ onChange={setClassMappings}
589
+ />
590
+ </div>
591
+ )}
559
592
  </div>
560
593
  )}
561
594