Compare commits
No commits in common. "bcc86be6c4d07cb7fec9c8983310653f9cbf27ce" and "e07547c44909c1323b55d758d2917d79dc3618f5" have entirely different histories.
bcc86be6c4
...
e07547c449
40
README.md
40
README.md
@ -1,37 +1,15 @@
|
|||||||
# Travel Journal (Draft README)
|
# Journey Mapper Backend
|
||||||
## Projectdescription
|
|
||||||
On this website you can document your travelstories.
|
|
||||||
Where you where, what you did and what videos and pictures you took.
|
|
||||||
Keep your journal and share it with friends or just log it for your memory.
|
|
||||||
|
|
||||||
## Pages
|
A Flask server for the Journey Mapper web application.
|
||||||
### Map Page
|
|
||||||
Here you can create a journey through setting marker on a map.
|
|
||||||
At each marker you can write notes or implement images.
|
|
||||||
|
|
||||||
### Blog Page
|
|
||||||
Here you can look at all notes from one journey.
|
|
||||||
The markers have a date so they are in a time order.
|
|
||||||
|
|
||||||
### Journey Explorer
|
|
||||||
Here you can search for all journeys. Like overview page from a blogwebsite.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- Blog entry
|
|
||||||
- Log places
|
|
||||||
- Add pictures to specific places
|
|
||||||
|
|
||||||
## Installation
|
- RESTful API for managing journeys
|
||||||
TBD
|
- Persistent storage using JSON file
|
||||||
|
- CORS enabled for frontend integration
|
||||||
|
- Health check endpoint
|
||||||
|
- Easy to set up and run
|
||||||
|
|
||||||
## Tech-Stack
|
## Getting Started
|
||||||
- HTML
|
|
||||||
- CSS
|
|
||||||
- JavaScript
|
|
||||||
- WebGL - MapLibre
|
|
||||||
|
|
||||||
## Members
|
|
||||||
- Flepp Stiafen
|
|
||||||
- Kohler Joshua
|
|
||||||
- Rüegger André
|
|
||||||
|
|
||||||
|
1. Install dependencies:
|
||||||
|
|||||||
@ -134,86 +134,5 @@ def index():
|
|||||||
</html>
|
</html>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
# Blog posts data file
|
|
||||||
BLOG_DATA_FILE = os.path.join(DATA_DIR, 'blog_posts.json')
|
|
||||||
|
|
||||||
def load_blog_posts():
|
|
||||||
try:
|
|
||||||
if os.path.exists(BLOG_DATA_FILE):
|
|
||||||
with open(BLOG_DATA_FILE, 'r') as f:
|
|
||||||
return json.load(f)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return []
|
|
||||||
|
|
||||||
def save_blog_posts(posts):
|
|
||||||
with open(BLOG_DATA_FILE, 'w') as f:
|
|
||||||
json.dump(posts, f, indent=2)
|
|
||||||
|
|
||||||
blog_posts = load_blog_posts()
|
|
||||||
|
|
||||||
def get_next_blog_id():
|
|
||||||
if not blog_posts:
|
|
||||||
return 1
|
|
||||||
return max(p['id'] for p in blog_posts) + 1
|
|
||||||
|
|
||||||
@app.route('/api/blog-posts', methods=['GET'])
|
|
||||||
def get_blog_posts():
|
|
||||||
return jsonify(blog_posts)
|
|
||||||
|
|
||||||
@app.route('/api/blog-posts/<int:post_id>', methods=['GET'])
|
|
||||||
def get_blog_post(post_id):
|
|
||||||
post = next((p for p in blog_posts if p['id'] == post_id), None)
|
|
||||||
if not post:
|
|
||||||
return jsonify({'error': 'Post not found'}), 404
|
|
||||||
return jsonify(post)
|
|
||||||
|
|
||||||
@app.route('/api/blog-posts', methods=['POST'])
|
|
||||||
def create_blog_post():
|
|
||||||
data = request.get_json()
|
|
||||||
title = data.get('title')
|
|
||||||
if not title:
|
|
||||||
return jsonify({'error': 'Title required'}), 400
|
|
||||||
new_post = {
|
|
||||||
'id': get_next_blog_id(),
|
|
||||||
'title': title,
|
|
||||||
'content': data.get('content', ''),
|
|
||||||
'journeyId': data.get('journeyId'),
|
|
||||||
'image': data.get('image'),
|
|
||||||
'created_at': datetime.now().isoformat()
|
|
||||||
}
|
|
||||||
blog_posts.append(new_post)
|
|
||||||
save_blog_posts(blog_posts)
|
|
||||||
return jsonify(new_post), 201
|
|
||||||
|
|
||||||
@app.route('/api/blog-posts/<int:post_id>', methods=['PUT'])
|
|
||||||
def update_blog_post(post_id):
|
|
||||||
post = next((p for p in blog_posts if p['id'] == post_id), None)
|
|
||||||
if not post:
|
|
||||||
return jsonify({'error': 'Post not found'}), 404
|
|
||||||
data = request.get_json()
|
|
||||||
if 'title' in data:
|
|
||||||
post['title'] = data['title']
|
|
||||||
if 'content' in data:
|
|
||||||
post['content'] = data['content']
|
|
||||||
if 'journeyId' in data:
|
|
||||||
post['journeyId'] = data['journeyId']
|
|
||||||
if 'image' in data:
|
|
||||||
post['image'] = data['image']
|
|
||||||
save_blog_posts(blog_posts)
|
|
||||||
return jsonify(post)
|
|
||||||
|
|
||||||
@app.route('/api/blog-posts/<int:post_id>', methods=['DELETE'])
|
|
||||||
def delete_blog_post(post_id):
|
|
||||||
global blog_posts
|
|
||||||
post = next((p for p in blog_posts if p['id'] == post_id), None)
|
|
||||||
if not post:
|
|
||||||
return jsonify({'error': 'Post not found'}), 404
|
|
||||||
blog_posts = [p for p in blog_posts if p['id'] != post_id]
|
|
||||||
save_blog_posts(blog_posts)
|
|
||||||
return jsonify({'message': 'Post deleted'})
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(debug=True, port=5000)
|
app.run(debug=True, port=5000)
|
||||||
@ -1,10 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"title": "test",
|
|
||||||
"content": "sfsfsfsaf",
|
|
||||||
"journeyId": "1",
|
|
||||||
"image": null,
|
|
||||||
"created_at": "2026-03-27T19:49:49.410806"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
233
blog-list.html
233
blog-list.html
@ -1,233 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Blog – 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 (same as map page) */
|
|
||||||
.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,
|
|
||||||
.site-nav a.active {
|
|
||||||
color: var(--indigo-4);
|
|
||||||
background: var(--surface-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Main content */
|
|
||||||
.blog-container {
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: var(--size-6) auto;
|
|
||||||
padding: 0 var(--size-4);
|
|
||||||
}
|
|
||||||
.blog-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: var(--size-6);
|
|
||||||
}
|
|
||||||
.blog-header h1 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: var(--font-size-6);
|
|
||||||
color: var(--indigo-8);
|
|
||||||
}
|
|
||||||
.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(--indigo-7);
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.btn:hover {
|
|
||||||
background: var(--indigo-8);
|
|
||||||
box-shadow: var(--shadow-3);
|
|
||||||
}
|
|
||||||
.posts-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
||||||
gap: var(--size-6);
|
|
||||||
}
|
|
||||||
.post-card {
|
|
||||||
background: var(--surface-1);
|
|
||||||
border-radius: var(--radius-3);
|
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: var(--shadow-2);
|
|
||||||
transition: transform 0.2s;
|
|
||||||
}
|
|
||||||
.post-card:hover {
|
|
||||||
transform: translateY(-4px);
|
|
||||||
}
|
|
||||||
.post-card-image {
|
|
||||||
width: 100%;
|
|
||||||
height: 200px;
|
|
||||||
object-fit: cover;
|
|
||||||
background: var(--surface-3);
|
|
||||||
}
|
|
||||||
.post-card-content {
|
|
||||||
padding: var(--size-4);
|
|
||||||
}
|
|
||||||
.post-card-title {
|
|
||||||
margin: 0 0 var(--size-2) 0;
|
|
||||||
font-size: var(--font-size-4);
|
|
||||||
}
|
|
||||||
.post-card-title a {
|
|
||||||
color: var(--gray-9);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.post-card-title a:hover {
|
|
||||||
color: var(--indigo-7);
|
|
||||||
}
|
|
||||||
.post-card-meta {
|
|
||||||
color: var(--gray-6);
|
|
||||||
font-size: var(--font-size-1);
|
|
||||||
margin-bottom: var(--size-3);
|
|
||||||
}
|
|
||||||
.post-card-excerpt {
|
|
||||||
color: var(--gray-7);
|
|
||||||
line-height: var(--font-lineheight-3);
|
|
||||||
}
|
|
||||||
.empty-state {
|
|
||||||
text-align: center;
|
|
||||||
color: var(--gray-6);
|
|
||||||
padding: var(--size-8);
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
</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" class="active">Blog</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main class="blog-container">
|
|
||||||
<div class="blog-header">
|
|
||||||
<h1><i class="fas fa-newspaper"></i> Blog Posts</h1>
|
|
||||||
<a href="blog-post.html?new" class="btn"><i class="fas fa-plus"></i> New Post</a>
|
|
||||||
</div>
|
|
||||||
<div id="posts-grid" class="posts-grid">
|
|
||||||
<!-- Posts loaded dynamically -->
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const API_BASE = 'http://127.0.0.1:5000/api';
|
|
||||||
|
|
||||||
async function loadPosts() {
|
|
||||||
try {
|
|
||||||
const res = await fetch(`${API_BASE}/blog-posts`);
|
|
||||||
if (!res.ok) throw new Error('Failed to fetch posts');
|
|
||||||
const posts = await res.json();
|
|
||||||
renderPosts(posts);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
document.getElementById('posts-grid').innerHTML = '<p class="empty-state">Failed to load posts. Make sure the backend is running.</p>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderPosts(posts) {
|
|
||||||
const container = document.getElementById('posts-grid');
|
|
||||||
if (!posts.length) {
|
|
||||||
container.innerHTML = '<p class="empty-state">No posts yet. Click "New Post" to create one.</p>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
container.innerHTML = posts.map(post => `
|
|
||||||
<article class="post-card">
|
|
||||||
${post.image ? `<img class="post-card-image" src="${post.image}" alt="${post.title}">` : '<div class="post-card-image" style="background: var(--surface-3); display: flex; align-items: center; justify-content: center;"><i class="fas fa-image" style="font-size: 3rem; color: var(--gray-5);"></i></div>'}
|
|
||||||
<div class="post-card-content">
|
|
||||||
<h2 class="post-card-title"><a href="blog-post.html?id=${post.id}">${escapeHtml(post.title)}</a></h2>
|
|
||||||
<div class="post-card-meta">
|
|
||||||
<i class="fas fa-calendar-alt"></i> ${new Date(post.created_at).toLocaleDateString()}
|
|
||||||
${post.journeyId ? `<span style="margin-left: 12px;"><i class="fas fa-route"></i> Journey #${post.journeyId}</span>` : ''}
|
|
||||||
</div>
|
|
||||||
<div class="post-card-excerpt">${escapeHtml(post.excerpt || post.content.substring(0, 150) + '…')}</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
`).join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
function escapeHtml(str) {
|
|
||||||
if (!str) return '';
|
|
||||||
return str.replace(/[&<>]/g, function(m) {
|
|
||||||
if (m === '&') return '&';
|
|
||||||
if (m === '<') return '<';
|
|
||||||
if (m === '>') return '>';
|
|
||||||
return m;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', loadPosts);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
290
blog-page.html
290
blog-page.html
@ -3,303 +3,33 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Blog Post – Journey Mapper</title>
|
<title>Blog Post</title>
|
||||||
|
|
||||||
<!-- Open Props CSS -->
|
<!-- Open Props CSS -->
|
||||||
<link rel="stylesheet" href="https://unpkg.com/open-props"/>
|
<link rel="stylesheet" href="https://unpkg.com/open-props"/>
|
||||||
|
<!-- optional: normalize / additional props -->
|
||||||
<link rel="stylesheet" href="https://unpkg.com/open-props/normalize.min.css"/>
|
<link rel="stylesheet" href="https://unpkg.com/open-props/normalize.min.css"/>
|
||||||
|
|
||||||
<!-- Font Awesome -->
|
<!-- Font Awesome -->
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
|
||||||
<!-- Google Fonts: Poppins (matching map page) -->
|
<!-- Google Fonts: Poppins -->
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
<style>
|
<link rel="stylesheet" href="css/style.css">
|
||||||
/* ===== GLOBAL RESET USING OPEN PROPS ===== */
|
<link rel="stylesheet" href="css/blog.css">
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
font-family: var(--font-sans);
|
|
||||||
background: var(--gray-0);
|
|
||||||
color: var(--gray-9);
|
|
||||||
line-height: var(--font-lineheight-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===== 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: 1200px;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
.site-nav a:hover {
|
|
||||||
color: var(--indigo-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===== MAIN LAYOUT (two‑column) ===== */
|
|
||||||
.post-page {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--size-6);
|
|
||||||
padding: var(--size-6) var(--size-4);
|
|
||||||
max-width: 1400px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post {
|
|
||||||
background: var(--surface-1);
|
|
||||||
border-radius: var(--radius-3);
|
|
||||||
padding: var(--size-6);
|
|
||||||
box-shadow: var(--shadow-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-title {
|
|
||||||
font-size: var(--font-size-6);
|
|
||||||
font-weight: var(--font-weight-7);
|
|
||||||
margin: 0 0 var(--size-2) 0;
|
|
||||||
color: var(--gray-9);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-meta {
|
|
||||||
color: var(--gray-6);
|
|
||||||
font-size: var(--font-size-1);
|
|
||||||
margin-bottom: var(--size-4);
|
|
||||||
border-bottom: 1px solid var(--surface-4);
|
|
||||||
padding-bottom: var(--size-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-hero {
|
|
||||||
margin: var(--size-4) 0;
|
|
||||||
}
|
|
||||||
.post-hero img {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
border-radius: var(--radius-2);
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-content {
|
|
||||||
line-height: var(--font-lineheight-4);
|
|
||||||
color: var(--gray-7);
|
|
||||||
}
|
|
||||||
.post-content p {
|
|
||||||
margin-bottom: var(--size-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sidebar */
|
|
||||||
.sidebar {
|
|
||||||
background: var(--surface-1);
|
|
||||||
border-radius: var(--radius-3);
|
|
||||||
padding: var(--size-4);
|
|
||||||
box-shadow: var(--shadow-2);
|
|
||||||
}
|
|
||||||
.sidebar section {
|
|
||||||
margin-bottom: var(--size-6);
|
|
||||||
}
|
|
||||||
.sidebar h3 {
|
|
||||||
font-size: var(--font-size-3);
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: var(--size-3);
|
|
||||||
color: var(--indigo-7);
|
|
||||||
border-left: 3px solid var(--indigo-6);
|
|
||||||
padding-left: var(--size-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Posts list */
|
|
||||||
#posts-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--size-3);
|
|
||||||
}
|
|
||||||
.post-preview {
|
|
||||||
background: var(--surface-2);
|
|
||||||
border-radius: var(--radius-2);
|
|
||||||
padding: var(--size-3);
|
|
||||||
transition: background 0.2s;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.post-preview:hover {
|
|
||||||
background: var(--surface-3);
|
|
||||||
}
|
|
||||||
.post-preview-title {
|
|
||||||
font-weight: var(--font-weight-6);
|
|
||||||
color: var(--gray-9);
|
|
||||||
margin: 0 0 var(--size-1) 0;
|
|
||||||
}
|
|
||||||
.post-preview-meta {
|
|
||||||
font-size: var(--font-size-0);
|
|
||||||
color: var(--gray-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Form for creating/editing posts */
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: var(--size-3);
|
|
||||||
}
|
|
||||||
.form-group label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: var(--size-1);
|
|
||||||
font-weight: var(--font-weight-5);
|
|
||||||
color: var(--gray-7);
|
|
||||||
}
|
|
||||||
.form-group input,
|
|
||||||
.form-group textarea {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
.form-group input:focus,
|
|
||||||
.form-group textarea:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--indigo-6);
|
|
||||||
box-shadow: 0 0 0 3px var(--indigo-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Buttons (reuse map page classes) */
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
.btn-primary {
|
|
||||||
background: var(--indigo-7);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.btn-primary:hover {
|
|
||||||
background: var(--indigo-8);
|
|
||||||
box-shadow: var(--shadow-3);
|
|
||||||
}
|
|
||||||
.btn-secondary {
|
|
||||||
background: var(--gray-7);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.btn-secondary:hover {
|
|
||||||
background: var(--gray-8);
|
|
||||||
}
|
|
||||||
.btn-danger {
|
|
||||||
background: var(--red-7);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.btn-danger:hover {
|
|
||||||
background: var(--red-8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Comments section */
|
|
||||||
.comments {
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: var(--size-6) auto 0;
|
|
||||||
padding: 0 var(--size-4);
|
|
||||||
}
|
|
||||||
.comments h3 {
|
|
||||||
font-size: var(--font-size-4);
|
|
||||||
margin-bottom: var(--size-4);
|
|
||||||
color: var(--indigo-7);
|
|
||||||
}
|
|
||||||
.comments-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--size-4);
|
|
||||||
}
|
|
||||||
.comment-item {
|
|
||||||
background: var(--surface-1);
|
|
||||||
border-radius: var(--radius-2);
|
|
||||||
padding: var(--size-4);
|
|
||||||
box-shadow: var(--shadow-1);
|
|
||||||
}
|
|
||||||
.comment-item .meta {
|
|
||||||
color: var(--gray-6);
|
|
||||||
font-size: var(--font-size-0);
|
|
||||||
margin-bottom: var(--size-2);
|
|
||||||
}
|
|
||||||
.comment-item p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Footer */
|
|
||||||
.site-footer {
|
|
||||||
background: var(--surface-2);
|
|
||||||
padding: var(--size-4);
|
|
||||||
margin-top: var(--size-6);
|
|
||||||
text-align: center;
|
|
||||||
color: var(--gray-6);
|
|
||||||
font-size: var(--font-size-1);
|
|
||||||
border-top: 1px solid var(--surface-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive two‑column layout */
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.post-page {
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: var(--size-6);
|
|
||||||
padding: var(--size-6);
|
|
||||||
}
|
|
||||||
.post {
|
|
||||||
flex: 1;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.sidebar {
|
|
||||||
width: 320px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
position: sticky;
|
|
||||||
top: var(--size-4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="site-header">
|
<header class="site-header">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="site-title"><a href="map-page.html">Journey Mapper</a></h1>
|
<h1 class="site-title"><a href="/">My Blog</a></h1>
|
||||||
<nav class="site-nav">
|
<nav class="site-nav">
|
||||||
<a href="map-page.html">Map</a>
|
<a href="map-page.html">Map</a>
|
||||||
<a href="blog-page.html" class="active">Blog</a>
|
<a href="blog-page.html">Blog</a>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="post-page">
|
<main class="container post-page">
|
||||||
<article class="post">
|
<article class="post">
|
||||||
<h2 class="post-title">Post Title</h2>
|
<h2 class="post-title">Post Title</h2>
|
||||||
<div class="post-meta">By <span class="author">Author</span> — <time datetime="2026-01-01">Jan 1, 2026</time></div>
|
<div class="post-meta">By <span class="author">Author</span> — <time datetime="2026-01-01">Jan 1, 2026</time></div>
|
||||||
@ -310,7 +40,6 @@
|
|||||||
<p>This is a placeholder paragraph for the blog post content. Replace with real content.</p>
|
<p>This is a placeholder paragraph for the blog post content. Replace with real content.</p>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<aside class="sidebar">
|
<aside class="sidebar">
|
||||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
|
||||||
<h3 style="margin:0">Posts</h3>
|
<h3 style="margin:0">Posts</h3>
|
||||||
@ -354,7 +83,7 @@
|
|||||||
</aside>
|
</aside>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<section class="comments">
|
<section class="comments container">
|
||||||
<h3>Comments</h3>
|
<h3>Comments</h3>
|
||||||
<div class="comments-list"></div>
|
<div class="comments-list"></div>
|
||||||
</section>
|
</section>
|
||||||
@ -363,7 +92,6 @@
|
|||||||
<div class="container">© 2026 My Blog</div>
|
<div class="container">© 2026 My Blog</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<!-- JavaScript (keep your existing logic) -->
|
|
||||||
<script src="js/main.js"></script>
|
<script src="js/main.js"></script>
|
||||||
<script src="js/blog-posts.js"></script>
|
<script src="js/blog-posts.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -1,347 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@ -479,64 +479,9 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.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,
|
|
||||||
.site-nav a.active {
|
|
||||||
color: var(--indigo-4);
|
|
||||||
background: var(--surface-2);
|
|
||||||
}
|
|
||||||
.app-container {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body style="display: flex; flex-direction: column; min-height: 100vh;">
|
<body>
|
||||||
<header class="site-header">
|
|
||||||
<div class="container">
|
|
||||||
<h1 class="site-title"><a href="/">Journey Mapper</a></h1>
|
|
||||||
<nav class="site-nav">
|
|
||||||
<a href="map-page.html" class="active">Map</a>
|
|
||||||
<a href="blog-list.html">Blog</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<!-- SIDEBAR -->
|
<!-- SIDEBAR -->
|
||||||
<aside class="sidebar">
|
<aside class="sidebar">
|
||||||
@ -639,7 +584,7 @@
|
|||||||
<div class="sidebar-footer">
|
<div class="sidebar-footer">
|
||||||
<div class="navigation">
|
<div class="navigation">
|
||||||
<a href="#" class="nav-link"><i class="fas fa-list"></i> All Journeys</a>
|
<a href="#" class="nav-link"><i class="fas fa-list"></i> All Journeys</a>
|
||||||
<a href="blog-page.html" class="nav-link"><i class="fas fa-blog"></i> Blog</a>
|
<a href="#" class="nav-link"><i class="fas fa-question-circle"></i> Help</a>
|
||||||
</div>
|
</div>
|
||||||
<p class="footer-text">Journey Mapper v1.0</p>
|
<p class="footer-text">Journey Mapper v1.0</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user