wiki-plugin-shoppe 0.0.32 → 0.0.34

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/client/shoppe.js CHANGED
@@ -44,6 +44,10 @@
44
44
  .sw-status.success { background: #d1fae5; color: #065f46; display: block; }
45
45
  .sw-status.error { background: #fee2e2; color: #991b1b; display: block; }
46
46
  .sw-status code { background: rgba(0,0,0,0.08); border-radius: 4px; padding: 1px 5px; font-size: 12px; }
47
+ .sw-progress-bar-track { background: rgba(0,0,0,0.12); border-radius: 4px; height: 8px; margin: 8px 0; overflow: hidden; }
48
+ .sw-progress-bar-fill { height: 100%; background: #1a56db; border-radius: 4px; width: 0%; transition: width 0.25s ease; }
49
+ .sw-progress-meta { display: flex; justify-content: space-between; font-size: 12px; opacity: 0.75; }
50
+ .sw-progress-item { margin-top: 6px; font-size: 13px; font-style: italic; }
47
51
  .sw-remove { display: block; width: 100%; margin-top: 24px; padding: 8px; background: none; border: 1px solid #e5e5ea; border-radius: 8px; font-size: 12px; color: #aaa; cursor: pointer; text-align: center; }
48
52
  .sw-remove:hover { border-color: #cc0000; color: #cc0000; }
49
53
  </style>
@@ -313,25 +317,72 @@
313
317
  // ── Upload ──────────────────────────────────────────────────────────────────
314
318
 
315
319
  async function uploadArchive(file, container) {
320
+ const statusEl = container.querySelector('#sw-upload-status');
321
+
322
+ // Step 1: POST the file, get a jobId back immediately.
316
323
  showStatus(container, '#sw-upload-status', `⏳ Uploading <strong>${file.name}</strong>…`, 'info');
317
324
  const form = new FormData();
318
325
  form.append('archive', file);
326
+ let jobId;
319
327
  try {
320
328
  const resp = await fetch('/plugin/shoppe/upload', { method: 'POST', body: form });
321
329
  const result = await resp.json();
322
- if (!result.success) throw new Error(result.error || 'Upload failed');
330
+ if (!result.success || !result.jobId) throw new Error(result.error || 'Upload failed');
331
+ jobId = result.jobId;
332
+ } catch (err) {
333
+ showStatus(container, '#sw-upload-status', `❌ ${err.message}`, 'error');
334
+ return;
335
+ }
336
+
337
+ // Step 2: Show progress UI and open SSE stream.
338
+ statusEl.className = 'sw-status info';
339
+ statusEl.style.display = 'block';
340
+ statusEl.innerHTML = `
341
+ <div id="sw-progress-title" style="font-weight:600;margin-bottom:6px;">⏳ Processing archive…</div>
342
+ <div class="sw-progress-bar-track"><div id="sw-progress-fill" class="sw-progress-bar-fill"></div></div>
343
+ <div class="sw-progress-meta">
344
+ <span id="sw-progress-count">0 / …</span>
345
+ <span id="sw-progress-pct">0%</span>
346
+ </div>
347
+ <div id="sw-progress-item" class="sw-progress-item"></div>
348
+ `;
349
+
350
+ const fillEl = statusEl.querySelector('#sw-progress-fill');
351
+ const countEl = statusEl.querySelector('#sw-progress-count');
352
+ const pctEl = statusEl.querySelector('#sw-progress-pct');
353
+ const itemEl = statusEl.querySelector('#sw-progress-item');
354
+ const titleEl = statusEl.querySelector('#sw-progress-title');
355
+
356
+ const es = new EventSource(`/plugin/shoppe/upload/progress/${jobId}`);
357
+
358
+ es.addEventListener('start', e => {
359
+ const { total, name } = JSON.parse(e.data);
360
+ titleEl.innerHTML = `⏳ Uploading <strong>${name}</strong> — ${total} item${total !== 1 ? 's' : ''}`;
361
+ countEl.textContent = `0 / ${total}`;
362
+ });
363
+
364
+ es.addEventListener('progress', e => {
365
+ const { current, total, label } = JSON.parse(e.data);
366
+ const pct = total > 0 ? Math.round(current / total * 100) : 0;
367
+ fillEl.style.width = pct + '%';
368
+ countEl.textContent = `${current} / ${total}`;
369
+ pctEl.textContent = pct + '%';
370
+ itemEl.textContent = label;
371
+ });
323
372
 
373
+ es.addEventListener('complete', e => {
374
+ es.close();
375
+ const result = JSON.parse(e.data);
324
376
  const r = result.results;
325
377
  const counts = [
326
- r.books.length && `📚 ${r.books.length} book${r.books.length !== 1 ? 's' : ''}`,
327
- r.music.length && `🎵 ${r.music.length} music item${r.music.length !== 1 ? 's' : ''}`,
328
- r.posts.length && `📝 ${r.posts.length} post${r.posts.length !== 1 ? 's' : ''}`,
329
- r.albums.length && `🖼️ ${r.albums.length} album${r.albums.length !== 1 ? 's' : ''}`,
330
- r.products.length && `📦 ${r.products.length} product${r.products.length !== 1 ? 's' : ''}`,
378
+ r.books && r.books.length && `📚 ${r.books.length} book${r.books.length !== 1 ? 's' : ''}`,
379
+ r.music && r.music.length && `🎵 ${r.music.length} music item${r.music.length !== 1 ? 's' : ''}`,
380
+ r.posts && r.posts.length && `📝 ${r.posts.length} post${r.posts.length !== 1 ? 's' : ''}`,
381
+ r.albums && r.albums.length && `🖼️ ${r.albums.length} album${r.albums.length !== 1 ? 's' : ''}`,
382
+ r.products && r.products.length && `📦 ${r.products.length} product${r.products.length !== 1 ? 's' : ''}`,
331
383
  r.appointments && r.appointments.length && `📅 ${r.appointments.length} appointment${r.appointments.length !== 1 ? 's' : ''}`,
332
384
  r.subscriptions && r.subscriptions.length && `🎁 ${r.subscriptions.length} subscription tier${r.subscriptions.length !== 1 ? 's' : ''}`
333
385
  ].filter(Boolean).join(' · ') || 'no items found';
334
-
335
386
  const warnings = (r.warnings && r.warnings.length > 0)
336
387
  ? `<br><br>⚠️ <strong>Warnings (${r.warnings.length}):</strong><br>${r.warnings.map(w => `• ${w}`).join('<br>')}`
337
388
  : '';
@@ -340,9 +391,14 @@
340
391
  <a href="/plugin/shoppe/${result.tenant.uuid}" target="_blank" class="sw-link" style="display:inline-block;margin-top:8px;">View your shoppe →</a>`,
341
392
  'success');
342
393
  loadDirectory(container);
343
- } catch (err) {
344
- showStatus(container, '#sw-upload-status', `❌ ${err.message}`, 'error');
345
- }
394
+ });
395
+
396
+ es.addEventListener('error', e => {
397
+ es.close();
398
+ let msg = 'Upload failed';
399
+ try { msg = JSON.parse(e.data).message; } catch (err) { /* use default */ }
400
+ showStatus(container, '#sw-upload-status', `❌ ${msg}`, 'error');
401
+ });
346
402
  }
347
403
 
348
404
  // ── Save URL (owner) ────────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wiki-plugin-shoppe",
3
- "version": "0.0.32",
3
+ "version": "0.0.34",
4
4
  "description": "Multi-tenant digital goods shoppe for federated wiki, powered by Sanora",
5
5
  "keywords": [
6
6
  "wiki",