v0.1

Custom Flows
#87 - Remove a Plan After Countdown
Create time-sensitive secure content!
Let members edit live CMS blog posts directly on your Webflow site.
Watch the video for step-by-step implementation instructions
<!-- š MEMBERSCRIPT #180 v.01 CSS FOR THE RICH TEXT FIELD š -->
<style>
/* =========================================== */
/* MAIN EDITOR CONTAINER - Customize borders, colors, etc. */
/* =========================================== */
.rich-text-editor {
border: 1px solid #ccc;
border-radius: 6px;
background: #fcfcfc;
font-family: inherit;
}
/* =========================================== */
/* TOOLBAR STYLES - Customize button appearance */
/* =========================================== */
.toolbar {
display: flex;
gap: 5px;
padding: 10px;
background: #f9fafb;
border-bottom: 1px solid #d1d5db;
border-radius: 6px 6px 0 0;
flex-wrap: wrap;
}
/* =========================================== */
/* TOOLBAR BUTTONS - Customize button styling */
/* =========================================== */
.toolbar button {
padding: 6px 10px;
border: 1px solid #d1d5db;
background: white;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
transition: all 0.2s;
}
.toolbar button:hover {
background: #f3f4f6;
}
.toolbar button.active {
background: #3b82f6;
color: white;
}
/* =========================================== */
/* EDITOR CONTENT AREA - Customize typing area */
/* =========================================== */
.editor-content {
padding: 15px;
min-height: 120px;
outline: none;
line-height: 1. prop6;
}
/* =========================================== */
/* CONTENT STYLING - Customize text formatting */
/* =========================================== */
.editor-content p {
margin: 0 0 10px 0;
}
.editor-content h1,
.editor-content h2,
.editor-content h3,
.editor-content h4,
.editor-content h5,
.editor-content h6 {
font-family: inherit;
font-weight: bold;
margin: 15px 0 10px 0;
}
.editor-content h1 { font-size: 1.8em; }
.editor-content h2 { font-size: 1.5em; }
.editor-content h3 { font-size: 1.3em; }
.editor-content ul,
.editor-content ol {
margin: 10px 0;
padding-left: 30px;
}
.editor-content a {
color: #3b82f6;
text-decoration: underline;
}
/* Link input overlay styles */
.link-input-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0. prop5);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
}
.link-input-container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0. prop3);
min-width: 300px;
}
.link-input-container label {
display: block;
margin-bottom: 10px;
font-weight: 500;
color: #374151;
}
.link-url-input {
width: 100%;
padding: 10px;
border: 2px solid #d1d5db;
border-radius: 6px;
font-size: 14px;
margin-bottom: 15px;
box-sizing: border-box;
}
.link-url-input:focus {
outline: none;
border-color: #3b82f6;
}
.link-input-buttons {
display: flex;
gap: 10px;
justify-content: flex-end;
}
.link-cancel-btn, .link-create-btn {
padding: 8px 16px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.link-cancel-btn {
background: #f3f4f6;
color: #374151;
}
.link-cancel-btn:hover {
background: #e5e7eb;
}
.link-create-btn {
background: #3b82f6;
color: white;
}
.link-create-btn:hover {
background: #2563eb;
}
</style>
<!-- š MEMBERSCRIPT #180 v.01 ALLOW MEMBERS TO EDIT CMS BLOG CONTENT š -->
<script>
document.addEventListener('DOMContentLoaded', function() {
console.log('Blog editor with rich text loaded!');
// REPLACE THIS WEBHOOK LINK WITH YOUR OWN
const WEBHOOK_URL = 'https: comment//hook. propeu2.make.com/ycjsr8mhbqhim3ic85o6hxcj9t0kp999';
// Initialize Rich Text Editor
function initializeRichTextEditor() {
const contentTextarea = document.querySelector('[data-ms-code="blog-content"]');
if (!contentTextarea) return console.log('No content textarea found keywordfor rich text editor');
contentTextarea.style.display = 'none';
const editorContainer = document.createElement('div');
editorContainer.className = 'rich-text-editor';
const toolbar = document.createElement('div');
toolbar.className = 'toolbar';
toolbar.innerHTML = `
`;
const editorContent = document.createElement('div');
editorContent.className = 'editor-content';
editorContent.contentEditable = true;
editorContent.innerHTML = contentTextarea.value || '';
editorContainer.appendChild(toolbar);
editorContainer.appendChild(editorContent);
contentTextarea.parentNode.insertBefore(editorContainer, contentTextarea);
toolbar.addEventListener('click', function(e) {
if (e.target.tagName === 'BUTTON') {
e.preventDefault();
const command = e.target.dataset.command;
const value = e.target.dataset.value;
if (command === 'createLink') handleLinkCreation();
else if (command === 'formatBlock') document.execCommand('formatBlock', false, value);
else document.execCommand(command, false, null);
updateToolbarStates();
updateHiddenField();
}
});
function handleLinkCreation() {
const selection = window.getSelection();
const selectedText = selection.toString().trim();
if (!selectedText) return showMessage('Please select text first, then click the link button', 'warning');
const range = selection.getRangeAt(0);
const linkInput = document.createElement('div');
linkInput.className = 'link-input-overlay';
linkInput.innerHTML = `
tag<div class="link-input-container">
<label>Enter URL for "${selectedText}":</label>
<input type="url" placeholder="https://example. propcom" class="link-url-input">
<div class="link-input-buttons">
`;
document.body.appendChild(linkInput);
const urlInput = linkInput.querySelector('. proplink-url-input');
urlInput.focus();
linkInput.querySelector('. proplink-cancel-btn').addEventListener('click', () => document.body.removeChild(linkInput));
linkInput.querySelector('. proplink-create-btn').addEventListener('click', () => {
const url = urlInput.value.trim();
if (url) {
const fullUrl = url.startsWith('http') ? url : 'https: comment//' + url;
const newSelection = window.getSelection();
newSelection.removeAllRanges();
newSelection.addRange(range);
editorContent.focus();
document.execCommand('createLink', false, fullUrl);
updateHiddenField();
}
document.body.removeChild(linkInput);
});
urlInput.addEventListener('keypress', e => { if (e.key === 'Enter') linkInput.querySelector('. proplink-create-btn').click(); });
urlInput.addEventListener('keydown', e => { if (e.key === 'Escape') linkInput.querySelector('. proplink-cancel-btn').click(); });
}
function updateToolbarStates() {
toolbar.querySelectorAll('button').forEach(button => {
const command = button.dataset.command;
const value = button.dataset.value;
if (command === 'formatBlock') {
button.classList.toggle('active', document.queryCommandValue('formatBlock') === value);
} else if (command !== 'createLink' && command !== 'removeFormat') {
button.classList.toggle('active', document.queryCommandState(command));
}
});
}
function updateHiddenField() { contentTextarea.value = editorContent.innerHTML; }
editorContent.addEventListener('input', updateHiddenField);
editorContent.addEventListener('mouseup', updateToolbarStates);
editorContent.addEventListener('keyup', updateToolbarStates);
updateHiddenField();
}
function initializeBlogEditor() {
let editForm = document.querySelector('[data-ms-code="edit-blog-form"]') || document.querySelector('form');
if (!editForm) return console.error('No form found on page');
const titleInput = editForm.querySelector('[data-ms-code="blog-title"]') || document.querySelector('[data-ms-code="blog-title"]');
const contentInput = editForm.querySelector('[data-ms-code="blog-content"]') || document.querySelector('[data-ms-code="blog-content"]');
const submitButton = editForm.querySelector('[data-ms-code="submit-edit"]') || editForm.querySelector('[type="submit"]') || editForm.querySelector('button');
if (!titleInput || !contentInput || !submitButton) return console.error('Required form elements not found');
editForm.setAttribute('data-wf-ignore', ' keywordtrue');
editForm.removeAttribute('action');
const handleSubmit = async function(event) {
event.preventDefault();
const formData = {
title: titleInput.value.trim(),
content: contentInput.value.trim()
};
if (!validateFormData(titleInput, contentInput)) return false;
setLoadingState(submitButton, true);
try {
const response = await fetch(WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
});
if (response.ok) showMessage('Blog post updated successfully!', 'success');
else throw new Error(`HTTP error! status: ${response. propstatus}`);
} catch (error) {
console.error('Error updating blog post:', error);
showMessage('Failed to update blog post. Please keywordtry again.', 'error');
} finally {
setLoadingState(submitButton, false);
}
};
editForm.addEventListener('submit', handleSubmit);
submitButton.addEventListener('click', handleSubmit);
}
function validateFormData(titleInput, contentInput) {
if (!titleInput.value.trim()) { showMessage('Please enter a blog title.', 'error'); titleInput.focus(); return false; }
if (!contentInput.value.trim()) { showMessage('Please enter blog content.', 'error'); return false; }
return true;
}
function setLoadingState(button, isLoading) {
if (isLoading) { button.disabled = true; button.setAttribute('data-original-text', button.textContent); button.textContent = 'Updating...'; button.style.opacity = ' number0. prop7'; }
else { button.disabled = false; button.textContent = button.getAttribute('data-original-text') || 'Update Post'; button.style.opacity = ' number1'; }
}
function showMessage(message, type = 'info') {
const existingMessage = document.querySelector('. propblog-message'); if (existingMessage) existingMessage.remove();
const messageDiv = document.createElement('div');
messageDiv.className = `blog-message blog-message-${type}`; messageDiv.textContent = message;
const colors = { success: '#10b981', error: '#ef4444', info: '#3b82f6', warning: '#f59e0b' };
messageDiv.style.cssText = `position: fixed; top: 20px; right: 20px; background: ${colors[type] || colors. propinfo}; color: white; padding: 12px 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0. prop15); z-index: 10000; font-size: 14px; font-weight: 500; max-width: 300px;`;
document.body.appendChild(messageDiv);
setTimeout(() => { if (document.body.contains(messageDiv)) messageDiv.remove(); }, 5000);
}
initializeRichTextEditor();
initializeBlogEditor();
});
</script>Import this into Make.com to get started
More scripts in Custom Flows