175 lines
5.7 KiB
JavaScript

/* Minimal blog-posts.js
- Stores posts in localStorage under `blogPosts`
- Posts: {id, title, content, image, journeyId, created_at}
- Supports create, edit, delete, view
*/
const STORAGE_KEY = 'blogPosts';
function loadPosts() {
try {
return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
} catch (e) {
console.error('Failed to parse posts', e);
return [];
}
}
function savePosts(posts) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(posts));
}
function getNextId(posts) {
if (!posts || posts.length === 0) return 1;
return Math.max(...posts.map(p => p.id)) + 1;
}
function renderPostList() {
const posts = loadPosts().sort((a,b)=> new Date(b.created_at) - new Date(a.created_at));
const container = document.getElementById('posts-list');
container.innerHTML = '';
if (posts.length === 0) {
container.innerHTML = '<p class="empty-message">No posts yet.</p>';
return;
}
posts.forEach(post => {
const el = document.createElement('div');
el.className = 'marker-item';
el.innerHTML = `
<div style="display:flex;justify-content:space-between;align-items:center;gap:8px;">
<div style="flex:1">
<strong>${escapeHtml(post.title)}</strong>
<div style="font-size:.85rem;color:#7f8c8d">${new Date(post.created_at).toLocaleString()}${post.journeyId ? ' • Journey '+post.journeyId : ''}</div>
</div>
<div style="display:flex;gap:6px">
<button class="btn btn-small" data-action="view" data-id="${post.id}">View</button>
<button class="btn btn-small" data-action="edit" data-id="${post.id}">Edit</button>
<button class="btn btn-small btn-danger" data-action="delete" data-id="${post.id}">Delete</button>
</div>
</div>
`;
container.appendChild(el);
});
}
function escapeHtml(s){
return String(s || '').replace(/[&<>"']/g, (m)=>({
'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'
}[m]));
}
function showPost(id) {
const posts = loadPosts();
const post = posts.find(p=>p.id===id);
if (!post) return;
const titleEl = document.querySelector('.post-title');
const metaEl = document.querySelector('.post-meta');
const imgEl = document.querySelector('.post-hero img');
const contentEl = document.querySelector('.post-content');
titleEl.textContent = post.title;
metaEl.innerHTML = `By <span class="author">Author</span> — <time datetime="${post.created_at}">${new Date(post.created_at).toLocaleString()}</time>`;
if (post.image) imgEl.src = post.image; else imgEl.src = 'https://via.placeholder.com/800x350?text=Hero+Image';
contentEl.innerHTML = post.content;
}
function openForm(mode='create', post=null) {
const formWrap = document.getElementById('post-form');
formWrap.style.display = 'block';
document.getElementById('form-title').textContent = mode === 'create' ? 'New Post' : 'Edit Post';
const form = document.getElementById('blog-form');
form.dataset.mode = mode;
form.dataset.id = post ? post.id : '';
document.getElementById('post-title-input').value = post ? post.title : '';
document.getElementById('post-journey-input').value = post ? (post.journeyId || '') : '';
document.getElementById('post-image-input').value = post ? (post.image || '') : '';
document.getElementById('post-content-input').value = post ? post.content : '';
}
function closeForm() {
const formWrap = document.getElementById('post-form');
formWrap.style.display = 'none';
const form = document.getElementById('blog-form');
form.removeAttribute('data-id');
}
function deletePost(id) {
if (!confirm('Delete this post?')) return;
let posts = loadPosts();
posts = posts.filter(p=>p.id!==id);
savePosts(posts);
renderPostList();
// if the shown post was deleted, clear article
const currentTitle = document.querySelector('.post-title').textContent;
if (!posts.find(p=>p.title===currentTitle)) {
document.querySelector('.post-title').textContent = 'Post Title';
document.querySelector('.post-content').innerHTML = '<p>No post selected.</p>';
}
}
document.addEventListener('DOMContentLoaded', ()=>{
renderPostList();
document.getElementById('create-post-btn').addEventListener('click', ()=> openForm('create'));
document.getElementById('cancel-post-btn').addEventListener('click', (e)=>{ e.preventDefault(); closeForm(); });
document.getElementById('posts-list').addEventListener('click', (e)=>{
const btn = e.target.closest('button[data-action]');
if (!btn) return;
const id = parseInt(btn.dataset.id,10);
const action = btn.dataset.action;
if (action === 'view') showPost(id);
if (action === 'edit') {
const posts = loadPosts();
const post = posts.find(p=>p.id===id);
openForm('edit', post);
}
if (action === 'delete') deletePost(id);
});
document.getElementById('blog-form').addEventListener('submit', (e)=>{
e.preventDefault();
const form = e.target;
const mode = form.dataset.mode || 'create';
const id = parseInt(form.dataset.id,10) || null;
const title = document.getElementById('post-title-input').value.trim();
const journeyId = document.getElementById('post-journey-input').value.trim() || null;
const image = document.getElementById('post-image-input').value.trim() || '';
const content = document.getElementById('post-content-input').value.trim();
let posts = loadPosts();
if (mode === 'create') {
const newPost = {
id: getNextId(posts),
title,
content,
image,
journeyId: journeyId || null,
created_at: new Date().toISOString()
};
posts.push(newPost);
savePosts(posts);
renderPostList();
showPost(newPost.id);
} else if (mode === 'edit' && id) {
const idx = posts.findIndex(p=>p.id===id);
if (idx !== -1) {
posts[idx].title = title;
posts[idx].content = content;
posts[idx].image = image;
posts[idx].journeyId = journeyId || null;
savePosts(posts);
renderPostList();
showPost(id);
}
}
closeForm();
});
});