From 511639ed15d35382682a81b03c60c973f41efd01 Mon Sep 17 00:00:00 2001 From: Till Tomczak Date: Sun, 20 Apr 2025 18:48:14 +0200 Subject: [PATCH] Add user authentication, thoughts, and comments functionality; enhance mindmap visualization and UI --- README.md | 1 + requirements.txt | 7 +- website/__pycache__/app.cpython-311.pyc | Bin 0 -> 18039 bytes website/__pycache__/init_db.cpython-311.pyc | Bin 0 -> 5066 bytes website/app.py | 253 ++++++- website/init_db.py | 88 +++ website/instance/mindmap.db | Bin 0 -> 36864 bytes website/requirements.txt | 6 + website/run.py | 11 + website/static/background.js | 108 +++ website/templates/admin.html | 109 +++ website/templates/base.html | 126 ++++ website/templates/index.html | 57 +- website/templates/login.html | 41 ++ website/templates/mindmap.html | 706 +++++++++++++++++++- website/templates/profile.html | 131 ++++ website/templates/register.html | 47 ++ 17 files changed, 1656 insertions(+), 35 deletions(-) create mode 100644 README.md create mode 100644 website/__pycache__/app.cpython-311.pyc create mode 100644 website/__pycache__/init_db.cpython-311.pyc create mode 100644 website/init_db.py create mode 100644 website/instance/mindmap.db create mode 100644 website/requirements.txt create mode 100644 website/run.py create mode 100644 website/static/background.js create mode 100644 website/templates/admin.html create mode 100644 website/templates/base.html create mode 100644 website/templates/login.html create mode 100644 website/templates/profile.html create mode 100644 website/templates/register.html diff --git a/README.md b/README.md new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/README.md @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 8ab6294..dda7386 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,6 @@ -flask \ No newline at end of file +flask +flask-login +flask-wtf +email-validator +python-dotenv +flask-sqlalchemy \ No newline at end of file diff --git a/website/__pycache__/app.cpython-311.pyc b/website/__pycache__/app.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..edb745545799d49c19f01eb85eaf4e41811e185b GIT binary patch literal 18039 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 W|ADI?>$Co$j~N?q{NXqa@c#hFBaV3h literal 0 HcmV?d00001 diff --git a/website/__pycache__/init_db.cpython-311.pyc b/website/__pycache__/init_db.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5cedb6517f8ec5f20cfc47bfd8f888fd9a2c0131 GIT binary patch literal 5066 zcmb7|OH3Qv8Gy(3_yKdwBQ}FcE@2>{T$6+d15L|?)PzUgkTj%GB3-$T=MYb@$L`EH z1a~4)=`Ls$sa;fRmR_{!t&#;xq;?T0n=H?ukrIuRRmGyaRB5H?uKLf6jXii$!T@K^ z{O3FW<2--y*KoL%f+u#upZ$9~Mg7zKs6D=F?s@Xwd)~b7 zp3g(^-tVY;H1_kpRj=7Y`iYmq^mC}aFh8xwLxC*7)6YF1g+GVX?5AVWh#x+hA&+#8eaasLEo0KWH8qSk-wfUE*5y4sT#IkK`6}-KPEzEc*>qz6!mbg z?hIps@=%JuuH<2Dh$trq6zHry5BGXh0ySCV+u3*HjRF%no7b4^!x?PAC2N822S`QUXPz|XqYU=^f^r&=GG>pTOqb>w;5XW4Iu!H!@ zg=jleG>nSWu-bM&G`*_7DVlbNC&yig4hPZaLUcL^&V@K~sA$^N4z*J~azHfwYM?2a zh{KZs7b5B)PPh!5%7_h#FP9RQ7;q2G!vHXyE*7JOzGqATgx2AE4vw1BtjR z;eG=g{tO>>o}>Ft_*45^d5`(yJR2dg@!XkGuiZtk`pp85N65+vBo_HZ8;iQOhd$L4ICEZUdg<5jX*IiVNo%ShT_^ zMZv_+aSqHs5|r0}R%BRM1xdi=$n~`8ya)!d%w2;efJGy6tFWT1!2rStt_hjdTuFRj zFmo`O;{CL^3Vsb$=c2yuIQQA^hhFyTtC_*wnYQGZpYs`L)8$qirHCn4A zYL&5CrOG5~Od?ff5~(VaNL87!8Z%a7#%j!1jTx^o<27cy#*C-NWpmAN?#~bLv)Mu( zpUo5`e3r+L&aUD6GAxh1w>c9j{{BdD-3Z(D>KsW6pw*lV`6f)j2DRIQp7v=iNA;GY zAE;{{|Cn9whFYJ7f7|}FeY5?)0Sa|IoqWq2+hLArOs~%L5@zr{>Lf>oHFQcxrwBT= z8;wJLvecvWA9rG)&O&d(`&y%I_C{xA<&AL;X7H zC#WAR^z>F%$oK-e`3n-eqs8v%u{)cW-=VIzXkZ5oRDPg=qocApY3eIWBChMLrpp>wYw$OVmE(Afpc zHW^C3=_2fs#xCjXQdP8%`T|=nnk#EtCBjZ??6l5K+j0JSdxEet8at!2Gq&W>7AEYJ z#!l(%6i5!R6r#ehXQ{0_1WjvbT1V4Rd5^kD@7VT57~aNl4#!Au0!AOf=+EkC7Ah9m zEPM9t7GSU%zR}S)P=QGbx4SR&`jXiVg-P3}7E0)$1PLWR-1XKswY|0fZEf@aG;H?9 zjsLm&r%#_g*VHJNkHFFL$E8y@kdGkxMn{lDJWv{;R0=WyonWCF?G-t`zIMG~GANq*78G;`>|HyxV;)BoQ@w}sk$w9v#=tYkQPThv`mtKlSo8=u}5ApT9 zqdPaQ|7PjCrH!R`bj!v(X-#VMj84xGdd3oLp3&%-PR9rxs{%jP=yN)Kj?m|-f^m(W U(CG(%32PP1$TmS$7 literal 0 HcmV?d00001 diff --git a/website/app.py b/website/app.py index 148ed5b..92c3370 100644 --- a/website/app.py +++ b/website/app.py @@ -1,17 +1,264 @@ -from flask import Flask, render_template +import os +from datetime import datetime +from flask import Flask, render_template, request, redirect, url_for, flash, jsonify +from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user +from flask_sqlalchemy import SQLAlchemy +from werkzeug.security import generate_password_hash, check_password_hash +import json app = Flask(__name__) +app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'default-dev-key') +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///mindmap.db' +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False -# Route für die Startseite +db = SQLAlchemy(app) +login_manager = LoginManager(app) +login_manager.login_view = 'login' + +# Database Models +class User(UserMixin, db.Model): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(80), unique=True, nullable=False) + email = db.Column(db.String(120), unique=True, nullable=False) + password_hash = db.Column(db.String(128)) + is_admin = db.Column(db.Boolean, default=False) + thoughts = db.relationship('Thought', backref='author', lazy=True) + + def set_password(self, password): + self.password_hash = generate_password_hash(password) + + def check_password(self, password): + return check_password_hash(self.password_hash, password) + +class Thought(db.Model): + id = db.Column(db.Integer, primary_key=True) + content = db.Column(db.Text, nullable=False) + timestamp = db.Column(db.DateTime, default=datetime.utcnow) + branch = db.Column(db.String(100), nullable=False) + user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) + comments = db.relationship('Comment', backref='thought', lazy=True, cascade="all, delete-orphan") + +class Comment(db.Model): + id = db.Column(db.Integer, primary_key=True) + content = db.Column(db.Text, nullable=False) + timestamp = db.Column(db.DateTime, default=datetime.utcnow) + thought_id = db.Column(db.Integer, db.ForeignKey('thought.id'), nullable=False) + user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) + author = db.relationship('User', backref='comments') + +class MindMapNode(db.Model): + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(100), nullable=False) + parent_id = db.Column(db.Integer, db.ForeignKey('mind_map_node.id'), nullable=True) + children = db.relationship('MindMapNode', backref=db.backref('parent', remote_side=[id])) + thoughts = db.relationship('Thought', secondary='node_thought_association', backref='nodes') + +# Association table for many-to-many relationship between MindMapNode and Thought +node_thought_association = db.Table('node_thought_association', + db.Column('node_id', db.Integer, db.ForeignKey('mind_map_node.id'), primary_key=True), + db.Column('thought_id', db.Integer, db.ForeignKey('thought.id'), primary_key=True) +) + +@login_manager.user_loader +def load_user(id): + return User.query.get(int(id)) + +# Routes for authentication +@app.route('/login', methods=['GET', 'POST']) +def login(): + if request.method == 'POST': + username = request.form.get('username') + password = request.form.get('password') + + user = User.query.filter_by(username=username).first() + if user and user.check_password(password): + login_user(user) + next_page = request.args.get('next') + return redirect(next_page or url_for('index')) + flash('Ungültiger Benutzername oder Passwort') + return render_template('login.html') + +@app.route('/register', methods=['GET', 'POST']) +def register(): + if request.method == 'POST': + username = request.form.get('username') + email = request.form.get('email') + password = request.form.get('password') + + if User.query.filter_by(username=username).first(): + flash('Benutzername existiert bereits') + return redirect(url_for('register')) + + if User.query.filter_by(email=email).first(): + flash('E-Mail ist bereits registriert') + return redirect(url_for('register')) + + user = User(username=username, email=email) + user.set_password(password) + db.session.add(user) + db.session.commit() + + login_user(user) + return redirect(url_for('index')) + return render_template('register.html') + +@app.route('/logout') +@login_required +def logout(): + logout_user() + return redirect(url_for('index')) + +# Route for the homepage @app.route('/') def index(): return render_template('index.html') -# Route für die Mindmap-Seite +# Route for the mindmap page @app.route('/mindmap') def mindmap(): return render_template('mindmap.html') +# Route for user profile +@app.route('/profile') +@login_required +def profile(): + thoughts = Thought.query.filter_by(user_id=current_user.id).order_by(Thought.timestamp.desc()).all() + return render_template('profile.html', thoughts=thoughts) + +# API routes for mindmap and thoughts +@app.route('/api/mindmap') +def get_mindmap(): + root_nodes = MindMapNode.query.filter_by(parent_id=None).all() + + def build_tree(node): + return { + 'id': node.id, + 'name': node.name, + 'children': [build_tree(child) for child in node.children] + } + + result = [build_tree(node) for node in root_nodes] + return jsonify(result) + +@app.route('/api/thoughts/', methods=['GET']) +def get_thoughts(node_id): + node = MindMapNode.query.get_or_404(node_id) + thoughts = [] + + for thought in node.thoughts: + thoughts.append({ + 'id': thought.id, + 'content': thought.content, + 'author': thought.author.username, + 'timestamp': thought.timestamp.strftime('%d.%m.%Y, %H:%M'), + 'comments_count': len(thought.comments), + 'branch': thought.branch + }) + + return jsonify(thoughts) + +@app.route('/api/thought/', methods=['GET']) +def get_thought(thought_id): + thought = Thought.query.get_or_404(thought_id) + + return jsonify({ + 'id': thought.id, + 'content': thought.content, + 'author': thought.author.username, + 'timestamp': thought.timestamp.strftime('%d.%m.%Y, %H:%M'), + 'branch': thought.branch, + 'comments_count': len(thought.comments) + }) + +@app.route('/api/thoughts', methods=['POST']) +@login_required +def add_thought(): + data = request.json + node_id = data.get('node_id') + content = data.get('content') + + if not node_id or not content: + return jsonify({'error': 'Fehlende Daten'}), 400 + + node = MindMapNode.query.get_or_404(node_id) + + thought = Thought( + content=content, + branch=node.name, + user_id=current_user.id + ) + + db.session.add(thought) + node.thoughts.append(thought) + db.session.commit() + + return jsonify({ + 'id': thought.id, + 'content': thought.content, + 'author': thought.author.username, + 'timestamp': thought.timestamp.strftime('%d.%m.%Y, %H:%M'), + 'branch': thought.branch + }) + +@app.route('/api/comments/', methods=['GET']) +def get_comments(thought_id): + thought = Thought.query.get_or_404(thought_id) + comments = [ + { + 'id': comment.id, + 'content': comment.content, + 'author': comment.author.username, + 'timestamp': comment.timestamp.strftime('%d.%m.%Y, %H:%M') + } + for comment in thought.comments + ] + return jsonify(comments) + +@app.route('/api/comments', methods=['POST']) +@login_required +def add_comment(): + data = request.json + thought_id = data.get('thought_id') + content = data.get('content') + + if not thought_id or not content: + return jsonify({'error': 'Fehlende Daten'}), 400 + + thought = Thought.query.get_or_404(thought_id) + + comment = Comment( + content=content, + thought_id=thought_id, + user_id=current_user.id + ) + + db.session.add(comment) + db.session.commit() + + return jsonify({ + 'id': comment.id, + 'content': comment.content, + 'author': comment.author.username, + 'timestamp': comment.timestamp.strftime('%d.%m.%Y, %H:%M') + }) + +# Admin routes +@app.route('/admin') +@login_required +def admin(): + if not current_user.is_admin: + flash('Zugriff verweigert') + return redirect(url_for('index')) + + users = User.query.all() + nodes = MindMapNode.query.all() + thoughts = Thought.query.all() + + return render_template('admin.html', users=users, nodes=nodes, thoughts=thoughts) + # Flask starten if __name__ == '__main__': + with app.app_context(): + # Make sure tables exist + db.create_all() app.run(host="0.0.0.0", debug=True) \ No newline at end of file diff --git a/website/init_db.py b/website/init_db.py new file mode 100644 index 0000000..c5d930a --- /dev/null +++ b/website/init_db.py @@ -0,0 +1,88 @@ +from app import app, db, User, MindMapNode + +def init_database(): + """Initialize the database with admin user and mindmap structure.""" + with app.app_context(): + # Create all tables + db.create_all() + + # Check if we already have users + if User.query.first() is None: + print("Creating admin user...") + # Create admin user + admin = User(username='admin', email='admin@example.com', is_admin=True) + admin.set_password('admin123') + db.session.add(admin) + + # Create regular test user + test_user = User(username='test', email='test@example.com', is_admin=False) + test_user.set_password('test123') + db.session.add(test_user) + + db.session.commit() + print("Admin user created successfully!") + + # Check if we already have mindmap nodes + if MindMapNode.query.first() is None: + print("Creating initial mindmap structure...") + # Create initial mindmap structure + root = MindMapNode(name="Wissenschaftliche Mindmap") + db.session.add(root) + + # Level 1 nodes + node1 = MindMapNode(name="Naturwissenschaften", parent=root) + node2 = MindMapNode(name="Geisteswissenschaften", parent=root) + node3 = MindMapNode(name="Technologie", parent=root) + node4 = MindMapNode(name="Künste", parent=root) + db.session.add_all([node1, node2, node3, node4]) + + # Level 2 nodes - Naturwissenschaften + node1_1 = MindMapNode(name="Physik", parent=node1) + node1_2 = MindMapNode(name="Biologie", parent=node1) + node1_3 = MindMapNode(name="Chemie", parent=node1) + node1_4 = MindMapNode(name="Astronomie", parent=node1) + db.session.add_all([node1_1, node1_2, node1_3, node1_4]) + + # Level 2 nodes - Geisteswissenschaften + node2_1 = MindMapNode(name="Philosophie", parent=node2) + node2_2 = MindMapNode(name="Geschichte", parent=node2) + node2_3 = MindMapNode(name="Psychologie", parent=node2) + node2_4 = MindMapNode(name="Soziologie", parent=node2) + db.session.add_all([node2_1, node2_2, node2_3, node2_4]) + + # Level 2 nodes - Technologie + node3_1 = MindMapNode(name="Informatik", parent=node3) + node3_2 = MindMapNode(name="Biotechnologie", parent=node3) + node3_3 = MindMapNode(name="Künstliche Intelligenz", parent=node3) + node3_4 = MindMapNode(name="Energietechnik", parent=node3) + db.session.add_all([node3_1, node3_2, node3_3, node3_4]) + + # Level 2 nodes - Künste + node4_1 = MindMapNode(name="Bildende Kunst", parent=node4) + node4_2 = MindMapNode(name="Musik", parent=node4) + node4_3 = MindMapNode(name="Literatur", parent=node4) + node4_4 = MindMapNode(name="Film", parent=node4) + db.session.add_all([node4_1, node4_2, node4_3, node4_4]) + + # Level 3 nodes - a few examples + # Physik + MindMapNode(name="Quantenphysik", parent=node1_1) + MindMapNode(name="Relativitätstheorie", parent=node1_1) + + # Informatik + MindMapNode(name="Maschinelles Lernen", parent=node3_1) + MindMapNode(name="Softwareentwicklung", parent=node3_1) + MindMapNode(name="Datenbanken", parent=node3_1) + + # Commit changes + db.session.commit() + print("Mindmap structure created successfully!") + + print("Database initialization complete.") + +if __name__ == "__main__": + init_database() + print("You can now run the application with 'python app.py'") + print("Login with:") + print(" Admin: username=admin, password=admin123") + print(" User: username=test, password=test123") \ No newline at end of file diff --git a/website/instance/mindmap.db b/website/instance/mindmap.db new file mode 100644 index 0000000000000000000000000000000000000000..976a308eae47f5925df12b7e12537823061b163e GIT binary patch literal 36864 zcmeI)Pi)&%90zbaahg9}<3bljQ<*SgqOG(){+HP4RJOKcU1^)HSqkhFz1Yu-*Cg&@ zm#sS>t%EZsZd|x<;|d43aY5q1Z5K{RTo4jhI3b>$bV~}^_$#6AYsF6T-tRs8_jy)Q zyV1M8weAK~a=dPv1d_&0ay-vnmL!hjl59=0^>9hB7sBBi_RJ5w9`!QGov%+%h`(^D z%siLx?Q-EmzBnwX#%OTv{(nqoSnQOggG_ZE3YqE3cHRQf0Fy zRkqjH&q|r};SNVsU#qTeELLwwYvmi#Y`>qg(&I*RVINmEtL4>|N*Ht)GbdHcSIX6L zWx0G^YP%h~-X^>Cumfq4R6slKJUr{@s_O0kFJwn|6Z!Q*wy6Ul?kRkg~i^z*@)IP}pr# zKOpU0=`}W7wbhOCpK#yvX}3ONPeWt!_b`u+T7P##qZXF+q+>OuHx{eQR~M_ZiY(6!@%|I3r=@*ENakl|_=DR~+C!)D(3`{%!BEu` z&X^8q)6seDk}f-)2F`(S%$_#%j~We)B3xrbnS>=z zNFb~pO2RxcHndIL*5ee_Bh|a?Q|^w}wd+md4~(#CMxPI^Une#@bZKdGbG^J+8I}Kb zWp!)2EX^JsP1N|XFggO3?o50ve=#@evm<>8e?9>;{z^1AIh#!jY ziwB}FI^wptEUKa;irHVYKV-koewzI#`$6_S7K#M|5P$##AOHafKmY;|fB*y_@D~Kq zIf2XZSKL-Rkv%DJFY@6;bX^kcbrYE>fy?t7d%nAqn0j8|=J+MIWm7hBhP1ZV@q@(F zb75h*L%TO!8c?g*ad(6lCI#*yzxL%9ETZLFO)9OjIW1Z(_a^P!6Q;yoKVB5>wLI2R z5VHdJ62IE%O@GJw5GTTd>)t)L7ZIO4A#gAA*Zh5}*)I{t!;lr~TTQl;fW{{?QApEm zdA_&XbZJ~nharo8(DgcA+oiGbl)#{8KmY;|fB*y_009U<00Iy={sQ6rfB60X@gHYo2muH{ U00Izz00bZa0SG_<0?)R { + star.position.z += star.velocity; + + // Reset position if star moves too close + if (star.position.z > 100) { + star.position.z = -300; + } + }); + + // Rotate the entire scene slightly for a dreamy effect + scene.rotation.y += 0.0003; + scene.rotation.x += 0.0001; + + renderer.render(scene, camera); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); +} + +// Initialize background when the DOM is loaded +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initBackground); +} else { + initBackground(); +} \ No newline at end of file diff --git a/website/templates/admin.html b/website/templates/admin.html new file mode 100644 index 0000000..3726b05 --- /dev/null +++ b/website/templates/admin.html @@ -0,0 +1,109 @@ +{% extends "base.html" %} + +{% block title %}Admin | Wissenschaftliche Mindmap{% endblock %} + +{% block content %} +
+
+

Admin Bereich

+

Verwalte Benutzer, Gedanken und die Mindmap-Struktur.

+
+ +
+ +
+
+

Benutzer

+ {{ users|length }} +
+ +
+ + + + + + + + + + + {% for user in users %} + + + + + + + {% endfor %} + +
IDBenutzernameEmailRolle
{{ user.id }}{{ user.username }}{{ user.email }} + {% if user.is_admin %} + Admin + {% else %} + Benutzer + {% endif %} +
+
+
+ + +
+
+

Mindmap Struktur

+ {{ nodes|length }} +
+ +
+
+ {% for node in nodes %} +
+
+ {{ node.name }} + ID: {{ node.id }} +
+ {% if node.parent %} +

Eltern: {{ node.parent.name }}

+ {% else %} +

Hauptknoten

+ {% endif %} +
+ {% endfor %} +
+
+ +
+ +
+
+ + +
+
+

Gedanken

+ {{ thoughts|length }} +
+ +
+
+ {% for thought in thoughts %} +
+
+ {{ thought.branch }} + {{ thought.timestamp.strftime('%d.%m.%Y') }} +
+

{{ thought.content }}

+
+ Von: {{ thought.author.username }} + {{ thought.comments|length }} Kommentar(e) +
+
+ {% endfor %} +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/website/templates/base.html b/website/templates/base.html new file mode 100644 index 0000000..5231846 --- /dev/null +++ b/website/templates/base.html @@ -0,0 +1,126 @@ + + + + + + {% block title %}Wissenschaftliche Mindmap{% endblock %} + + + + + {% block extra_head %}{% endblock %} + + + +
+ +
+ + + + + {% with messages = get_flashed_messages() %} + {% if messages %} +
+ {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} +
+ {% endif %} + {% endwith %} + + +
+ {% block content %}{% endblock %} +
+
+ + + + + {% block scripts %}{% endblock %} + + \ No newline at end of file diff --git a/website/templates/index.html b/website/templates/index.html index 66b2c56..f4f66a9 100644 --- a/website/templates/index.html +++ b/website/templates/index.html @@ -1,18 +1,41 @@ - - - - - Wissenschaftliche Mindmap - - - - -
-

Willkommen zur Wissenschafts-Mindmap

-

Verknüpfe Wissen in neuronalen Strukturen.

- Starte die Mindmap +{% extends "base.html" %} + +{% block content %} +
+
+

Willkommen zur Wissenschafts-Mindmap

+

Verknüpfe Wissen in neuronalen Strukturen und teile deine Gedanken mit der Community.

+ +
+ + Starte die Mindmap + + {% if not current_user.is_authenticated %} + + Registrieren + + {% endif %} +
- - \ No newline at end of file + +
+
+
🧠
+

Visualisiere Wissen

+

Erkenne Zusammenhänge zwischen verschiedenen Wissensgebieten durch intuitive Mindmaps.

+
+ +
+
💡
+

Teile Gedanken

+

Füge deine eigenen Gedanken zu bestehenden Themen hinzu und bereichere die Community.

+
+ +
+
🔄
+

Interaktive Vernetzung

+

Beteilige dich an Diskussionen und sieh wie sich Ideen gemeinsam entwickeln.

+
+
+
+{% endblock %} \ No newline at end of file diff --git a/website/templates/login.html b/website/templates/login.html new file mode 100644 index 0000000..471d79b --- /dev/null +++ b/website/templates/login.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} + +{% block title %}Anmelden | Wissenschaftliche Mindmap{% endblock %} + +{% block content %} +
+
+

Anmelden

+ +
+
+ + +
+ +
+ + +
+ +
+ +
+
+ +
+

+ Noch kein Konto? + + Registrieren + +

+
+
+
+{% endblock %} \ No newline at end of file diff --git a/website/templates/mindmap.html b/website/templates/mindmap.html index 5d6438d..c36dc69 100644 --- a/website/templates/mindmap.html +++ b/website/templates/mindmap.html @@ -1,14 +1,692 @@ - - - - - Wissenschaftliche Mindmap - - - - -

Wissenschaftliche Mindmap

-
- - - \ No newline at end of file +{% extends "base.html" %} + +{% block title %}Mindmap | Wissenschaftliche Mindmap{% endblock %} + +{% block extra_head %} + +{% endblock %} + +{% block content %} +
+ +
+
+

Wissenschaftliche Mindmap

+
+ + +
+ + + +
+ + +
+

Kategorien

+
+
+ + Naturwissenschaften +
+
+ + Geisteswissenschaften +
+
+ + Technologie +
+
+ + Künste +
+
+
+ + +
+
+ + +
+
+
+

Keine Auswahl

+ +
+ + {% if current_user.is_authenticated %} +
+

Teile deinen Gedanken

+ + +
+ {% else %} +
+

Melde dich an, um deine Gedanken zu teilen

+ Anmelden +
+ {% endif %} + +

Community Gedanken

+
+
+

Wähle einen Knoten aus, um Gedanken zu sehen

+
+
+
+
+
+ + + +{% endblock %} + +{% block scripts %} + + +{% endblock %} \ No newline at end of file diff --git a/website/templates/profile.html b/website/templates/profile.html new file mode 100644 index 0000000..da208e1 --- /dev/null +++ b/website/templates/profile.html @@ -0,0 +1,131 @@ +{% extends "base.html" %} + +{% block title %}Profil | Wissenschaftliche Mindmap{% endblock %} + +{% block content %} +
+
+
+
+

Hallo, {{ current_user.username }}

+

{{ current_user.email }}

+
+ + +
+
+ +
+

Deine Gedanken

+ + {% if thoughts %} +
+ {% for thought in thoughts %} +
+
+ {{ thought.branch }} + {{ thought.timestamp.strftime('%d.%m.%Y, %H:%M') }} +
+

{{ thought.content }}

+ +
+
+ {{ thought.comments|length }} Kommentar(e) +
+ + Details anzeigen +
+
+ {% endfor %} +
+ {% else %} +
+

Du hast noch keine Gedanken geteilt.

+ Zur Mindmap gehen und mitmachen +
+ {% endif %} +
+
+ + + +{% endblock %} + +{% block scripts %} + +{% endblock %} \ No newline at end of file diff --git a/website/templates/register.html b/website/templates/register.html new file mode 100644 index 0000000..b4d6671 --- /dev/null +++ b/website/templates/register.html @@ -0,0 +1,47 @@ +{% extends "base.html" %} + +{% block title %}Registrieren | Wissenschaftliche Mindmap{% endblock %} + +{% block content %} +
+
+

Registrieren

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+ +
+

+ Bereits registriert? + + Anmelden + +

+
+
+
+{% endblock %} \ No newline at end of file