FHGR_Frontend_Group_3_Project/blog-post-edit.html
2026-03-27 20:14:20 +01:00

347 lines
12 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blog Post Journey Mapper</title>
<!-- Open Props CSS -->
<link rel="stylesheet" href="https://unpkg.com/open-props"/>
<link rel="stylesheet" href="https://unpkg.com/open-props/normalize.min.css"/>
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Google Fonts: Poppins -->
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
* { box-sizing: border-box; }
body {
font-family: var(--font-sans);
background: var(--gray-0);
color: var(--gray-9);
line-height: var(--font-lineheight-3);
margin: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
}
/* Header */
.site-header {
background: var(--gray-9);
padding: var(--size-4) var(--size-6);
border-bottom: 1px solid var(--surface-4);
}
.site-header .container {
max-width: 1400px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: var(--size-2);
}
.site-title {
margin: 0;
font-size: var(--font-size-4);
font-weight: var(--font-weight-6);
}
.site-title a {
color: var(--indigo-4);
text-decoration: none;
}
.site-nav {
display: flex;
gap: var(--size-4);
}
.site-nav a {
color: var(--gray-2);
text-decoration: none;
font-weight: var(--font-weight-5);
transition: color 0.2s;
padding: var(--size-1) var(--size-2);
border-radius: var(--radius-2);
}
.site-nav a:hover {
color: var(--indigo-4);
background: var(--surface-2);
}
/* Main content */
.post-container {
max-width: 800px;
margin: var(--size-6) auto;
padding: 0 var(--size-4);
}
.post-form {
background: var(--surface-1);
border-radius: var(--radius-3);
padding: var(--size-6);
box-shadow: var(--shadow-2);
}
.form-group {
margin-bottom: var(--size-4);
}
label {
display: block;
margin-bottom: var(--size-2);
font-weight: var(--font-weight-6);
color: var(--gray-8);
}
input[type="text"],
textarea,
select {
width: 100%;
padding: var(--size-2) var(--size-3);
border: 1px solid var(--surface-4);
border-radius: var(--radius-2);
background: var(--surface-2);
color: var(--text-1);
font-family: inherit;
font-size: var(--font-size-2);
}
textarea {
min-height: 200px;
resize: vertical;
}
.image-preview {
margin-top: var(--size-2);
max-width: 100%;
border-radius: var(--radius-2);
overflow: hidden;
}
.image-preview img {
max-width: 100%;
height: auto;
display: block;
}
.button-group {
display: flex;
gap: var(--size-3);
margin-top: var(--size-6);
}
.btn {
padding: var(--size-2) var(--size-4);
border: none;
border-radius: var(--radius-2);
cursor: pointer;
font-size: var(--font-size-2);
font-weight: var(--font-weight-5);
display: inline-flex;
align-items: center;
gap: var(--size-2);
transition: all 0.2s var(--ease-2);
box-shadow: var(--shadow-2);
background: var(--gray-7);
color: white;
text-decoration: none;
}
.btn-primary {
background: var(--indigo-7);
}
.btn-primary:hover {
background: var(--indigo-8);
}
.btn-danger {
background: var(--red-7);
}
.btn-danger:hover {
background: var(--red-8);
}
.btn:hover {
box-shadow: var(--shadow-3);
}
.toast {
position: fixed;
bottom: var(--size-4);
right: var(--size-4);
background: var(--green-7);
color: white;
padding: var(--size-2) var(--size-4);
border-radius: var(--radius-2);
display: none;
z-index: 1100;
}
</style>
</head>
<body>
<header class="site-header">
<div class="container">
<h1 class="site-title"><a href="map-page.html">Journey Mapper</a></h1>
<nav class="site-nav">
<a href="map-page.html">Map</a>
<a href="blog-list.html">Blog</a>
</nav>
</div>
</header>
<main class="post-container">
<div class="post-form">
<h2 id="form-title">Edit Post</h2>
<form id="post-form">
<div class="form-group">
<label for="post-title">Title</label>
<input type="text" id="post-title" required>
</div>
<div class="form-group">
<label for="post-content">Content</label>
<textarea id="post-content" rows="10" required></textarea>
</div>
<div class="form-group">
<label for="post-journey">Associated Journey ID (optional)</label>
<input type="text" id="post-journey" placeholder="e.g., 3">
</div>
<div class="form-group">
<label for="post-image">Image URL or Upload</label>
<input type="text" id="post-image-url" placeholder="https://...">
<div style="margin: 8px 0; text-align: center;">or</div>
<input type="file" id="image-upload" accept="image/*">
<div class="image-preview" id="image-preview"></div>
</div>
<div class="button-group">
<button type="submit" class="btn btn-primary"><i class="fas fa-save"></i> Save</button>
<button type="button" id="delete-post" class="btn btn-danger"><i class="fas fa-trash"></i> Delete</button>
<a href="blog-list.html" class="btn"><i class="fas fa-times"></i> Cancel</a>
</div>
</form>
</div>
</main>
<div id="toast" class="toast"></div>
<script>
const API_BASE = 'http://127.0.0.1:5000/api';
let currentPostId = null;
// Get post id from URL
const urlParams = new URLSearchParams(window.location.search);
const postId = urlParams.get('id');
const isNew = urlParams.has('new');
async function loadPost(id) {
try {
const res = await fetch(`${API_BASE}/blog-posts/${id}`);
if (!res.ok) throw new Error('Post not found');
const post = await res.json();
currentPostId = post.id;
document.getElementById('post-title').value = post.title;
document.getElementById('post-content').value = post.content;
document.getElementById('post-journey').value = post.journeyId || '';
document.getElementById('post-image-url').value = post.image || '';
updateImagePreview(post.image);
document.getElementById('form-title').textContent = 'Edit Post';
} catch (err) {
showToast('Error loading post', true);
}
}
function updateImagePreview(url) {
const preview = document.getElementById('image-preview');
if (url) {
preview.innerHTML = `<img src="${url}" alt="Preview" style="max-width:100%; border-radius: var(--radius-2);">`;
} else {
preview.innerHTML = '';
}
}
async function savePost(event) {
event.preventDefault();
const title = document.getElementById('post-title').value.trim();
const content = document.getElementById('post-content').value.trim();
const journeyId = document.getElementById('post-journey').value.trim();
let image = document.getElementById('post-image-url').value.trim();
if (!title || !content) {
showToast('Title and content are required');
return;
}
const payload = {
title,
content,
journeyId: journeyId || null,
image: image || null
};
try {
let url, method;
if (isNew) {
url = `${API_BASE}/blog-posts`;
method = 'POST';
} else {
url = `${API_BASE}/blog-posts/${currentPostId}`;
method = 'PUT';
}
const res = await fetch(url, {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!res.ok) throw new Error('Save failed');
const data = await res.json();
showToast('Post saved successfully');
// Redirect to the new post if it was created
if (isNew) {
window.location.href = `blog-post.html?id=${data.id}`;
} else {
// reload if needed
}
} catch (err) {
showToast('Error saving post', true);
}
}
async function deletePost() {
if (!currentPostId) return;
if (!confirm('Delete this post?')) return;
try {
const res = await fetch(`${API_BASE}/blog-posts/${currentPostId}`, { method: 'DELETE' });
if (!res.ok) throw new Error('Delete failed');
showToast('Post deleted');
setTimeout(() => { window.location.href = 'blog-list.html'; }, 1000);
} catch (err) {
showToast('Error deleting post', true);
}
}
function showToast(message, isError = false) {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.style.backgroundColor = isError ? 'var(--red-7)' : 'var(--green-7)';
toast.style.display = 'block';
setTimeout(() => { toast.style.display = 'none'; }, 3000);
}
// Image upload (convert to base64 and set as image URL)
document.getElementById('image-upload').addEventListener('change', function(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(evt) {
const base64 = evt.target.result;
document.getElementById('post-image-url').value = base64;
updateImagePreview(base64);
};
reader.readAsDataURL(file);
});
document.getElementById('post-form').addEventListener('submit', savePost);
document.getElementById('delete-post').addEventListener('click', deletePost);
if (!isNew && postId) {
loadPost(postId);
} else if (isNew) {
currentPostId = null;
document.getElementById('form-title').textContent = 'New Post';
document.getElementById('delete-post').style.display = 'none';
} else {
// No id and not new maybe go to list
window.location.href = 'blog-list.html';
}
</script>
</body>
</html>