From 724ff4c880e4e4cf0bb03a08272bb231836631cc Mon Sep 17 00:00:00 2001
From: bruno
Date: Tue, 14 Jul 2026 12:23:05 -0400
Subject: [PATCH] feat(v4.0): wire Share/Publish modal with real API calls
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- publishPage() → POST /api/pages/{id}/publish
- unpublishPage() → DELETE /api/pages/{id}/publish
- shareInvite() → POST /api/pages/{id}/share
- loadShares() → GET /api/pages/{id}/shares
- removeShare() → DELETE /api/pages/{id}/share/{sid}
- All buttons now call real API instead of local state toggle
---
app/templates/page_editor.html | 60 +++++++++++++++++++++++++++++++---
1 file changed, 56 insertions(+), 4 deletions(-)
diff --git a/app/templates/page_editor.html b/app/templates/page_editor.html
index cce88c9..c5fac7b 100644
--- a/app/templates/page_editor.html
+++ b/app/templates/page_editor.html
@@ -75,9 +75,9 @@ page_title %}{{ page.title }}{% endblock %} {% block topbar %}
class="sd-input"
placeholder="Add people, groups or emails..."
x-model="inviteEmail"
- @keydown.enter="invitePerson"
+ @keydown.enter="shareInvite()"
/>
-
Publish
@@ -354,7 +354,7 @@ page_title %}{{ page.title }}{% endblock %} {% block topbar %}
Unpublish
@@ -657,6 +657,58 @@ page_title %}{{ page.title }}{% endblock %} {% block topbar %}
clearTimeout(this._toastTimer);
this._toastTimer = setTimeout(() => { this.toastVisible = false; }, 2500);
},
+ publishPage() {
+ var self = this;
+ var csrf = document.cookie.match(/csrf_token=([^;]+)/);
+ fetch('/api/pages/' + this.pid + '/publish', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrf ? csrf[1] : '' }
+ }).then(function(r){ return r.json(); }).then(function(d){
+ if (d.status === 'ok') { self.pagePublished = true; self.publishedUrl = d.url || self.publishedUrl; self.showToast('Published to web'); }
+ else { self.showToast(d.detail || 'Publish failed'); }
+ }).catch(function(){ self.showToast('Publish failed'); });
+ },
+ unpublishPage() {
+ var self = this;
+ var csrf = document.cookie.match(/csrf_token=([^;]+)/);
+ fetch('/api/pages/' + this.pid + '/publish', {
+ method: 'DELETE',
+ headers: { 'X-CSRF-Token': csrf ? csrf[1] : '' }
+ }).then(function(r){ return r.json(); }).then(function(d){
+ if (d.status === 'ok') { self.pagePublished = false; self.publishedUrl = ''; self.showToast('Unpublished'); }
+ else { self.showToast(d.detail || 'Unpublish failed'); }
+ }).catch(function(){ self.showToast('Unpublish failed'); });
+ },
+ shareInvite() {
+ var self = this;
+ if (!this.inviteEmail.trim()) return;
+ var csrf = document.cookie.match(/csrf_token=([^;]+)/);
+ fetch('/api/pages/' + this.pid + '/share', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrf ? csrf[1] : '' },
+ body: JSON.stringify({ email: this.inviteEmail.trim(), permission: this.invitePermission })
+ }).then(function(r){ return r.json(); }).then(function(d){
+ if (d.status === 'ok') { self.inviteEmail = ''; self.showToast('Invitation sent'); self.loadShares(); }
+ else { self.showToast(d.detail || 'Share failed'); }
+ }).catch(function(){ self.showToast('Share failed'); });
+ },
+ loadShares() {
+ var self = this;
+ fetch('/api/pages/' + this.pid + '/shares').then(function(r){ return r.json(); }).then(function(d){
+ self.accessList = d.shares || [];
+ }).catch(function(){ self.accessList = []; });
+ },
+ removeShare(sid) {
+ var self = this;
+ var csrf = document.cookie.match(/csrf_token=([^;]+)/);
+ fetch('/api/pages/' + this.pid + '/share/' + sid, {
+ method: 'DELETE',
+ headers: { 'X-CSRF-Token': csrf ? csrf[1] : '' }
+ }).then(function(r){ return r.json(); }).then(function(d){
+ if (d.status === 'ok') { self.showToast('Share removed'); self.loadShares(); }
+ else { self.showToast(d.detail || 'Remove failed'); }
+ }).catch(function(){ self.showToast('Remove failed'); });
+ },
invitePerson(){if(!this.inviteEmail.trim())return;this.accessList.push({email:this.inviteEmail,permission:this.invitePermission,permOpen:false});this.inviteEmail='';},
removeAccess(email){this.accessList=this.accessList.filter(a=>a.email!==email);},
exportPage(){this.moreOpen=false;},