wiki-plugin-shoppe 0.0.25 → 0.0.27
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 +70 -10
- package/package.json +1 -1
- package/server/server.js +4 -1
package/client/shoppe.js
CHANGED
|
@@ -22,8 +22,11 @@
|
|
|
22
22
|
.sw-shoppe-left { display: flex; flex-direction: column; gap: 2px; }
|
|
23
23
|
.sw-shoppe-name { font-weight: 600; font-size: 15px; }
|
|
24
24
|
.sw-shoppe-code { font-size: 18px; letter-spacing: 4px; }
|
|
25
|
+
.sw-shoppe-actions { display: flex; align-items: center; gap: 12px; }
|
|
25
26
|
.sw-link { font-size: 13px; color: #0066cc; text-decoration: none; white-space: nowrap; }
|
|
26
27
|
.sw-link:hover { text-decoration: underline; }
|
|
28
|
+
.sw-btn-delete { background: none; border: none; color: #cc0000; font-size: 13px; cursor: pointer; padding: 0; white-space: nowrap; }
|
|
29
|
+
.sw-btn-delete:hover { text-decoration: underline; }
|
|
27
30
|
.sw-empty { font-size: 13px; color: #999; font-style: italic; }
|
|
28
31
|
.sw-drop { border: 2px dashed #ccc; border-radius: 12px; padding: 28px 20px; text-align: center; background: #fafafa; transition: border-color 0.2s, background 0.2s; cursor: pointer; }
|
|
29
32
|
.sw-drop.dragover { border-color: #0066cc; background: #e8f0fe; }
|
|
@@ -41,6 +44,8 @@
|
|
|
41
44
|
.sw-status.success { background: #d1fae5; color: #065f46; display: block; }
|
|
42
45
|
.sw-status.error { background: #fee2e2; color: #991b1b; display: block; }
|
|
43
46
|
.sw-status code { background: rgba(0,0,0,0.08); border-radius: 4px; padding: 1px 5px; font-size: 12px; }
|
|
47
|
+
.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
|
+
.sw-remove:hover { border-color: #cc0000; color: #cc0000; }
|
|
44
49
|
</style>
|
|
45
50
|
|
|
46
51
|
<!-- Directory -->
|
|
@@ -161,37 +166,70 @@
|
|
|
161
166
|
<div id="sw-register-status" class="sw-status"></div>
|
|
162
167
|
</div>
|
|
163
168
|
|
|
169
|
+
<button class="sw-remove" id="sw-remove-btn">Remove plugin from page</button>
|
|
170
|
+
|
|
164
171
|
</div>
|
|
165
172
|
`;
|
|
166
173
|
|
|
174
|
+
div.querySelector('#sw-remove-btn').addEventListener('click', () => {
|
|
175
|
+
const $page = $item.parents('.page');
|
|
176
|
+
if (window.wiki && wiki.pageHandler && $page.length) {
|
|
177
|
+
wiki.pageHandler.put($page, { type: 'remove', id: item.id });
|
|
178
|
+
$item.remove();
|
|
179
|
+
} else if (window.wiki && wiki.textEditor) {
|
|
180
|
+
wiki.textEditor($item, item);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
167
184
|
setupListeners(div);
|
|
168
|
-
|
|
169
|
-
|
|
185
|
+
checkOwner(div).then(isOwner => {
|
|
186
|
+
if (!isOwner) loadDirectory(div, false);
|
|
187
|
+
});
|
|
170
188
|
},
|
|
171
189
|
|
|
172
|
-
bind: function($item, item) {
|
|
190
|
+
bind: function($item, item) {
|
|
191
|
+
$item.on('dblclick', () => {
|
|
192
|
+
const $page = $item.parents('.page');
|
|
193
|
+
if (window.wiki && wiki.pageHandler && $page.length) {
|
|
194
|
+
wiki.pageHandler.put($page, { type: 'remove', id: item.id });
|
|
195
|
+
$item.remove();
|
|
196
|
+
} else if (window.wiki && wiki.textEditor) {
|
|
197
|
+
wiki.textEditor($item, item);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
173
201
|
};
|
|
174
202
|
|
|
175
|
-
// ── Directory
|
|
203
|
+
// ── Directory ────────────────────────────────────────────────────────────────
|
|
176
204
|
|
|
177
|
-
async function loadDirectory(container) {
|
|
205
|
+
async function loadDirectory(container, isOwner = false) {
|
|
178
206
|
const el = container.querySelector('#sw-directory');
|
|
179
207
|
try {
|
|
180
|
-
const resp = await fetch('/plugin/shoppe/directory');
|
|
208
|
+
const resp = await fetch(isOwner ? '/plugin/shoppe/tenants' : '/plugin/shoppe/directory');
|
|
181
209
|
const result = await resp.json();
|
|
182
|
-
|
|
210
|
+
const shoppes = isOwner ? result.tenants : result.shoppes;
|
|
211
|
+
if (!result.success || !shoppes || shoppes.length === 0) {
|
|
183
212
|
el.innerHTML = '<em class="sw-empty">No shoppes yet — be the first!</em>';
|
|
184
213
|
return;
|
|
185
214
|
}
|
|
186
|
-
el.innerHTML =
|
|
187
|
-
<div class="sw-shoppe">
|
|
215
|
+
el.innerHTML = shoppes.map(s => `
|
|
216
|
+
<div class="sw-shoppe" id="sw-shoppe-${s.uuid}">
|
|
188
217
|
<div class="sw-shoppe-left">
|
|
189
218
|
<span class="sw-shoppe-name">${s.name}</span>
|
|
190
219
|
<span class="sw-shoppe-code">${s.emojicode}</span>
|
|
191
220
|
</div>
|
|
192
|
-
<
|
|
221
|
+
<div class="sw-shoppe-actions">
|
|
222
|
+
<a class="sw-link" href="${s.url}" target="_blank">Visit shoppe →</a>
|
|
223
|
+
${isOwner ? `<button class="sw-btn-delete" data-uuid="${s.uuid}" data-name="${s.name}">Delete</button>` : ''}
|
|
224
|
+
</div>
|
|
193
225
|
</div>
|
|
194
226
|
`).join('');
|
|
227
|
+
|
|
228
|
+
if (isOwner) {
|
|
229
|
+
el.querySelectorAll('.sw-btn-delete').forEach(btn => {
|
|
230
|
+
btn.addEventListener('click', () => deleteShoppe(btn.dataset.uuid, btn.dataset.name, container));
|
|
231
|
+
});
|
|
232
|
+
}
|
|
195
233
|
} catch (err) {
|
|
196
234
|
el.innerHTML = '<em class="sw-empty">Could not load directory.</em>';
|
|
197
235
|
}
|
|
@@ -208,8 +246,30 @@
|
|
|
208
246
|
if (result.sanoraUrl) {
|
|
209
247
|
container.querySelector('#sw-url-input').value = result.sanoraUrl;
|
|
210
248
|
}
|
|
249
|
+
loadDirectory(container, true);
|
|
250
|
+
return true;
|
|
211
251
|
}
|
|
212
252
|
} catch (err) { /* not owner, stay hidden */ }
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ── Delete shoppe (owner) ────────────────────────────────────────────────────
|
|
257
|
+
|
|
258
|
+
async function deleteShoppe(uuid, name, container) {
|
|
259
|
+
if (!confirm(`Delete "${name}"? This will remove the shoppe and all its products from Sanora.`)) return;
|
|
260
|
+
|
|
261
|
+
const row = container.querySelector(`#sw-shoppe-${uuid}`);
|
|
262
|
+
if (row) row.style.opacity = '0.4';
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
const resp = await fetch(`/plugin/shoppe/${uuid}`, { method: 'DELETE' });
|
|
266
|
+
const result = await resp.json();
|
|
267
|
+
if (!result.success) throw new Error(result.error || 'Delete failed');
|
|
268
|
+
loadDirectory(container, true);
|
|
269
|
+
} catch (err) {
|
|
270
|
+
if (row) row.style.opacity = '1';
|
|
271
|
+
alert(`Could not delete shoppe: ${err.message}`);
|
|
272
|
+
}
|
|
213
273
|
}
|
|
214
274
|
|
|
215
275
|
// ── Listeners ───────────────────────────────────────────────────────────────
|
package/package.json
CHANGED
package/server/server.js
CHANGED
|
@@ -219,7 +219,7 @@ function generateBundleBuffer(tenant, ownerPrivateKey, ownerPubKey, wikiOrigin)
|
|
|
219
219
|
private: true,
|
|
220
220
|
description: 'Shoppe content folder',
|
|
221
221
|
dependencies: {
|
|
222
|
-
'sessionless-node': '
|
|
222
|
+
'sessionless-node': 'latest'
|
|
223
223
|
}
|
|
224
224
|
}, null, 2);
|
|
225
225
|
|
|
@@ -489,6 +489,7 @@ async function sanoraCreateProduct(tenant, title, category, description, price,
|
|
|
489
489
|
`${getSanoraUrl()}/user/${uuid}/product/${encodeURIComponent(title)}`,
|
|
490
490
|
{
|
|
491
491
|
method: 'PUT',
|
|
492
|
+
timeout: 15000,
|
|
492
493
|
headers: { 'Content-Type': 'application/json' },
|
|
493
494
|
body: JSON.stringify({
|
|
494
495
|
timestamp,
|
|
@@ -522,6 +523,7 @@ async function sanoraUploadArtifact(tenant, title, fileBuffer, filename, artifac
|
|
|
522
523
|
`${getSanoraUrl()}/user/${uuid}/product/${encodeURIComponent(title)}/artifact`,
|
|
523
524
|
{
|
|
524
525
|
method: 'PUT',
|
|
526
|
+
timeout: 30000,
|
|
525
527
|
headers: {
|
|
526
528
|
'x-pn-artifact-type': artifactType,
|
|
527
529
|
'x-pn-timestamp': timestamp,
|
|
@@ -551,6 +553,7 @@ async function sanoraUploadImage(tenant, title, imageBuffer, filename) {
|
|
|
551
553
|
`${getSanoraUrl()}/user/${uuid}/product/${encodeURIComponent(title)}/image`,
|
|
552
554
|
{
|
|
553
555
|
method: 'PUT',
|
|
556
|
+
timeout: 30000,
|
|
554
557
|
headers: {
|
|
555
558
|
'x-pn-timestamp': timestamp,
|
|
556
559
|
'x-pn-signature': signature,
|