Bulletins have a section on dashboard now.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from fastapi import APIRouter, Path, Query, Depends, HTTPException, Request, status
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi import APIRouter, Path, Query, Depends, HTTPException, Request, status, Form
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from typing import Optional, List
|
||||
from pydantic import BaseModel, Field, constr
|
||||
from datetime import datetime
|
||||
@@ -130,6 +130,48 @@ async def bulletin_list_page(
|
||||
{"request": request, "bulletins": bulletins, "current_user": current_user.username}
|
||||
)
|
||||
|
||||
@html_router.get("/bulletins/new", response_class=HTMLResponse)
|
||||
async def bulletin_new_form(
|
||||
request: Request,
|
||||
current_user: HttpUser = Depends(get_current_http_user) # require login, consistent with site
|
||||
):
|
||||
return templates.TemplateResponse(
|
||||
"bulletin_new.html",
|
||||
{"request": request, "error": None}
|
||||
)
|
||||
|
||||
@html_router.post("/bulletins/new")
|
||||
async def bulletin_new_submit(
|
||||
request: Request,
|
||||
subject: str = Form(...),
|
||||
body: str = Form(...),
|
||||
current_user: HttpUser = Depends(get_current_http_user)
|
||||
):
|
||||
if not subject.strip() or not body.strip():
|
||||
return templates.TemplateResponse(
|
||||
"bulletin_new.html",
|
||||
{"request": request, "error": "Subject and body are required."},
|
||||
status_code=400
|
||||
)
|
||||
from packetserver.runners.http_server import get_db_connection
|
||||
conn = get_db_connection()
|
||||
root = conn.root()
|
||||
|
||||
if 'bulletins' not in root:
|
||||
root['bulletins'] = PersistentList()
|
||||
|
||||
new_bulletin = Bulletin(
|
||||
author=current_user.username,
|
||||
subject=subject.strip(),
|
||||
text=body.strip()
|
||||
)
|
||||
|
||||
new_id = new_bulletin.write_new(root)
|
||||
|
||||
transaction.commit()
|
||||
|
||||
return RedirectResponse(url=f"/bulletins/{new_id}", status_code=303)
|
||||
|
||||
@html_router.get("/bulletins/{bid}", response_class=HTMLResponse)
|
||||
async def bulletin_detail_page(
|
||||
request: Request,
|
||||
|
||||
@@ -10,6 +10,7 @@ router = APIRouter(tags=["dashboard"])
|
||||
|
||||
# Import the function at module level (safe now that circular import is fixed)
|
||||
from packetserver.http.routers.messages import get_messages as api_get_messages
|
||||
from .bulletins import list_bulletins
|
||||
|
||||
|
||||
@router.get("/dashboard", response_class=HTMLResponse)
|
||||
@@ -26,9 +27,17 @@ async def dashboard(
|
||||
)
|
||||
messages = messages_resp["messages"]
|
||||
|
||||
bulletins_resp = await list_bulletins(limit=10, since=None)
|
||||
recent_bulletins = bulletins_resp["bulletins"]
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"dashboard.html",
|
||||
{"request": request, "current_user": current_user.username, "messages": messages}
|
||||
{
|
||||
"request": request,
|
||||
"current_user": current_user.username,
|
||||
"messages": messages,
|
||||
"bulletins": recent_bulletins
|
||||
}
|
||||
)
|
||||
|
||||
@router.get("/dashboard/profile", response_class=HTMLResponse)
|
||||
|
||||
0
packetserver/http/static/css/style.css
Normal file
0
packetserver/http/static/css/style.css
Normal file
@@ -6,6 +6,12 @@
|
||||
<title>{% block title %}PacketServer Dashboard{% endblock %}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', path='/css/bootstrap.min.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', path='/css/style.css') }}"> {# optional custom #}
|
||||
<style>
|
||||
.bulletin-list { list-style: none; padding: 0; }
|
||||
.bulletin-list li { margin: 1em 0; padding: 1em; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
||||
.meta { color: #666; font-size: 0.9em; display: block; margin-top: 0.3em; }
|
||||
.preview { margin-top: 0.5em; color: #444; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<nav class="navbar navbar-dark bg-primary mb-4">
|
||||
|
||||
36
packetserver/http/templates/bulletin_new.html
Normal file
36
packetserver/http/templates/bulletin_new.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>New Bulletin - PacketServer</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; max-width: 800px; margin: 2em auto; padding: 1em; background: #f9f9f9; }
|
||||
h1 { text-align: center; }
|
||||
.error { color: #721c24; background: #f8d7da; padding: 1em; border-radius: 5px; margin-bottom: 1em; }
|
||||
label { display: block; margin: 1em 0 0.5em; font-weight: bold; }
|
||||
input[type="text"], textarea { width: 100%; padding: 0.8em; box-sizing: border-box; font-family: monospace; }
|
||||
textarea { height: 300px; }
|
||||
button { margin-top: 1em; padding: 0.8em 1.5em; background: #007bff; color: white; border: none; cursor: pointer; }
|
||||
button:hover { background: #0056b3; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Create New Bulletin</h1>
|
||||
|
||||
{% if error %}
|
||||
<div class="error">{{ error }}</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post">
|
||||
<label for="subject">Subject</label>
|
||||
<input type="text" id="subject" name="subject" required>
|
||||
|
||||
<label for="body">Body</label>
|
||||
<textarea id="body" name="body" required></textarea>
|
||||
|
||||
<button type="submit">Create Bulletin</button>
|
||||
</form>
|
||||
|
||||
<p><a href="/bulletins">← Back to Bulletins</a> | <a href="/dashboard">Dashboard</a></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,6 +3,24 @@
|
||||
{% block title %}Dashboard - {{ current_user }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Recent Bulletins</h2>
|
||||
|
||||
{% if bulletins %}
|
||||
<ul class="bulletin-list">
|
||||
{% for bull in bulletins %}
|
||||
<li>
|
||||
<strong><a href="/bulletins/{{ bull.id }}">{{ bull.subject }}</a></strong>
|
||||
<span class="meta">by {{ bull.author }} on {{ bull.created_at[:10] }}</span>
|
||||
<div class="preview">{{ bull.body[:150] }}{% if bull.body|length > 150 %}...{% endif %}</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p><a href="/bulletins">View all bulletins →</a></p>
|
||||
{% else %}
|
||||
<p>No bulletins yet.</p>
|
||||
<p><a href="/bulletins/new">Create the first one!</a></p>
|
||||
{% endif %}
|
||||
|
||||
<h2 class="mb-4">Messages</h2>
|
||||
|
||||
<!-- Compose Modal -->
|
||||
|
||||
Reference in New Issue
Block a user