Initial Commit
This commit is contained in:
2
modules/memos/__init__.py
Normal file
2
modules/memos/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from flask import Blueprint
|
||||
memos_bp = Blueprint("memos", __name__, template_folder="templates")
|
||||
27
modules/memos/models.py
Normal file
27
modules/memos/models.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from core.models import db
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class Memo(db.Model):
|
||||
__tablename__ = "memos"
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
memo = db.Column(db.Text, nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
|
||||
class Note(db.Model):
|
||||
__tablename__ = "notes"
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(255), unique=True, nullable=False)
|
||||
slug = db.Column(db.String(255), unique=True, nullable=False)
|
||||
note = db.Column(db.Text, nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
|
||||
class Journal(db.Model):
|
||||
__tablename__ = "journal"
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(255), unique=True, nullable=False)
|
||||
slug = db.Column(db.String(255), unique=True, nullable=False)
|
||||
entry = db.Column(db.Text, nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
70
modules/memos/routes.py
Normal file
70
modules/memos/routes.py
Normal file
@@ -0,0 +1,70 @@
|
||||
from flask import render_template, request, redirect, url_for
|
||||
from . import memos_bp
|
||||
from .models import db, Memo, Note, Journal
|
||||
from core.auth import require_perms
|
||||
|
||||
|
||||
@memos_bp.get("/")
|
||||
@require_perms("memos.read")
|
||||
def index():
|
||||
memos = Memo.query.order_by(Memo.created_at.desc()).all()
|
||||
return render_template("memos/index.html", memos=memos)
|
||||
|
||||
|
||||
@memos_bp.post("/add")
|
||||
@require_perms("memos.write")
|
||||
def add():
|
||||
v = (request.form.get("memo") or "").strip()
|
||||
if v:
|
||||
db.session.add(Memo(memo=v)); db.session.commit()
|
||||
return redirect(url_for("memos.index"))
|
||||
|
||||
|
||||
@memos_bp.get("/notes")
|
||||
@require_perms("memos.read")
|
||||
def notes():
|
||||
notes = Note.query.order_by(Note.created_at.desc()).all()
|
||||
return render_template("memos/notes.html", notes=notes)
|
||||
|
||||
|
||||
@memos_bp.post("/notes/new")
|
||||
@require_perms("memos.write")
|
||||
def notes_new():
|
||||
name = (request.form.get("name") or "").strip()
|
||||
text = (request.form.get("note") or "").strip()
|
||||
if name:
|
||||
slug = name.lower().replace(" ","-")
|
||||
db.session.add(Note(name=name, slug=slug, note=text)); db.session.commit()
|
||||
return redirect(url_for("memos.notes"))
|
||||
|
||||
|
||||
@memos_bp.get("/notes/<slug>")
|
||||
@require_perms("memos.read")
|
||||
def view_note(slug):
|
||||
n = Note.query.filter_by(slug=slug).first_or_404()
|
||||
return render_template("memos/view_note.html", note=n)
|
||||
|
||||
|
||||
@memos_bp.get("/journal")
|
||||
@require_perms("memos.read")
|
||||
def journal():
|
||||
entries = Journal.query.order_by(Journal.created_at.desc()).all()
|
||||
return render_template("memos/journal.html", journal=entries)
|
||||
|
||||
|
||||
@memos_bp.post("/journal/new")
|
||||
@require_perms("memos.write")
|
||||
def journal_new():
|
||||
name = (request.form.get("name") or "").strip()
|
||||
text = (request.form.get("entry") or "").strip()
|
||||
if name:
|
||||
slug = name.lower().replace(" ","-")
|
||||
db.session.add(Journal(name=name, slug=slug, entry=text)); db.session.commit()
|
||||
return redirect(url_for("memos.journal"))
|
||||
|
||||
|
||||
@memos_bp.get("/journal/<slug>")
|
||||
@require_perms("memos.read")
|
||||
def view_journal(slug):
|
||||
e = Journal.query.filter_by(slug=slug).first_or_404()
|
||||
return render_template("memos/view_journal.html", entry=e)
|
||||
18
modules/memos/templates/memos/index.html
Normal file
18
modules/memos/templates/memos/index.html
Normal file
@@ -0,0 +1,18 @@
|
||||
{% extends 'core/base.html' %}
|
||||
{% block title %}Memos — Portal{% endblock %}
|
||||
{% block content %}
|
||||
<section class="max-w-3xl mx-auto">
|
||||
<div class="card glass p-6">
|
||||
<h1 class="text-2xl font-bold">Memos</h1>
|
||||
<form method="post" action="/memos/add" class="mt-3 flex gap-2">
|
||||
<input name="memo" class="flex-1" placeholder="New memo…">
|
||||
<button class="btn">Add</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="mt-6 space-y-3">
|
||||
{% for m in memos %}
|
||||
<article class="card glass p-4"><div class="text-sm text-white/60">{{ m.created_at }}</div><div class="mt-1">{{ m.memo }}</div></article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
22
modules/memos/templates/memos/journal.html
Normal file
22
modules/memos/templates/memos/journal.html
Normal file
@@ -0,0 +1,22 @@
|
||||
{% extends 'core/base.html' %}
|
||||
{% block title %}Journal — Portal{% endblock %}
|
||||
{% block content %}
|
||||
<section class="max-w-4xl mx-auto grid md:grid-cols-2 gap-6">
|
||||
<div class="card glass p-6">
|
||||
<h1 class="text-xl font-semibold">New Entry</h1>
|
||||
<form method="post" action="/memos/journal/new" class="mt-3 grid gap-3">
|
||||
<input name="name" placeholder="Entry title" required>
|
||||
<textarea name="entry" rows="8" placeholder="What’s up…"></textarea>
|
||||
<button class="btn bg-accent font-semibold">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
{% for e in journal %}
|
||||
<a href="/memos/journal/{{ e.slug }}" class="block card glass p-4">
|
||||
<div class="text-sm text-white/60">{{ e.created_at }}</div>
|
||||
<div class="font-semibold">{{ e.name }}</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
22
modules/memos/templates/memos/notes.html
Normal file
22
modules/memos/templates/memos/notes.html
Normal file
@@ -0,0 +1,22 @@
|
||||
{% extends 'core/base.html' %}
|
||||
{% block title %}Notes — Portal{% endblock %}
|
||||
{% block content %}
|
||||
<section class="max-w-4xl mx-auto grid md:grid-cols-2 gap-6">
|
||||
<div class="card glass p-6">
|
||||
<h1 class="text-xl font-semibold">New Note</h1>
|
||||
<form method="post" action="/memos/notes/new" class="mt-3 grid gap-3">
|
||||
<input name="name" placeholder="Note title" required>
|
||||
<textarea name="note" rows="6" placeholder="Text…"></textarea>
|
||||
<button class="btn bg-accent font-semibold">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
{% for n in notes %}
|
||||
<a href="/memos/notes/{{ n.slug }}" class="block card glass p-4">
|
||||
<div class="text-sm text-white/60">{{ n.created_at }}</div>
|
||||
<div class="font-semibold">{{ n.name }}</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
8
modules/memos/templates/memos/view_journal.html
Normal file
8
modules/memos/templates/memos/view_journal.html
Normal file
@@ -0,0 +1,8 @@
|
||||
{% extends 'core/base.html' %}
|
||||
{% block title %}{{ entry.name }} — Journal{% endblock %}
|
||||
{% block content %}
|
||||
<article class="max-w-3xl mx-auto card glass p-6">
|
||||
<h1 class="text-2xl font-bold">{{ entry.name }}</h1>
|
||||
<div class="mt-3 whitespace-pre-wrap">{{ entry.entry }}</div>
|
||||
</article>
|
||||
{% endblock %}
|
||||
8
modules/memos/templates/memos/view_note.html
Normal file
8
modules/memos/templates/memos/view_note.html
Normal file
@@ -0,0 +1,8 @@
|
||||
{% extends 'core/base.html' %}
|
||||
{% block title %}{{ note.name }} — Note{% endblock %}
|
||||
{% block content %}
|
||||
<article class="max-w-3xl mx-auto card glass p-6">
|
||||
<h1 class="text-2xl font-bold">{{ note.name }}</h1>
|
||||
<div class="mt-3 whitespace-pre-wrap">{{ note.note }}</div>
|
||||
</article>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user