init project

This commit is contained in:
Jonas Heinrich 2025-08-21 09:40:22 +02:00
parent a26fa2c64a
commit dc0440fcef
291 changed files with 124865 additions and 485 deletions

View file

@ -1,198 +1,45 @@
{% load static %}
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Fragify{% endblock %}</title>
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/select2.min.css" rel="stylesheet">
<link href="/static/css/select2-bootstrap-5-theme.min.css" rel="stylesheet">
<meta name="description" content="{{ meta_description | default('Erstelle vorausgefüllte FragDenStaat.de-Anfragelinks und teile sie mit Freund:innen. Suche Behörden, füge Betreff und Text hinzu und generiere einen teilbaren Link.') }}">
<link rel="canonical" href="{{ canonical_url | default('/') }}">
<link rel="alternate" hreflang="de" href="{{ canonical_url | default('/') }}">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Fragify">
<meta property="og:title" content="{{ meta_title | default('Fragify') }}">
<meta property="og:description" content="{{ meta_description | default('Erstelle vorausgefüllte FragDenStaat.de-Anfragelinks und teile sie mit Freund:innen.') }}">
<meta property="og:locale" content="de_DE">
<meta property="og:url" content="{{ canonical_url | default('/') }}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ meta_title | default('Fragify') }}">
<meta name="twitter:description" content="{{ meta_description | default('Erstelle vorausgefüllte FragDenStaat.de-Anfragelinks und teile sie mit Freund:innen.') }}">
{% if noindex %}<meta name="robots" content="noindex,follow">{% endif %}
<meta name="theme-color" content="#667eea">
<link rel="icon" href="/static/favicon.svg" type="image/svg+xml">
<link rel="alternate icon" href="/static/favicon.ico">
{% block meta_extra %}{% endblock %}
<style>
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.main-container {
background: white;
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
padding: 3rem;
margin: 2rem auto;
max-width: 800px;
}
.title {
color: #2c3e50;
font-weight: 700;
margin-bottom: 1rem;
}
.description {
color: #7f8c8d;
font-size: 1.1rem;
margin-bottom: 2.5rem;
line-height: 1.6;
}
.form-control, .form-select {
border-radius: 10px;
border: 2px solid #e9ecef;
padding: 0.75rem 1rem;
transition: all 0.3s ease;
}
.form-control:focus, .form-select:focus {
border-color: #667eea;
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
}
.btn-primary {
background: linear-gradient(45deg, #667eea, #764ba2);
border: none;
border-radius: 15px;
padding: 1rem 2rem;
font-size: 1.1rem;
font-weight: 600;
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
}
.result-link {
background: #f8f9fa;
border: 2px solid #e9ecef;
border-radius: 10px;
padding: 1rem;
margin-top: 1rem;
word-break: break-all;
}
/* Ensure Select2 scales properly */
.select2-container {
width: 100% !important;
}
.select2-container .select2-selection--single {
height: auto;
min-height: 2.75rem;
}
.select2-container .select2-selection__rendered {
white-space: normal;
line-height: 1.4 !important;
}
.select2-container .select2-selection__arrow {
height: 100% !important;
}
/* Mobile adjustments */
@media (max-width: 576px) {
.main-container {
padding: 1.25rem;
margin: 1rem auto 4rem auto;
}
.title {
font-size: 2rem;
}
.description {
font-size: 1rem;
}
}
.loading {
display: none;
}
.footer {
background: transparent;
margin-top: 3rem;
}
.footer-links a {
font-size: 0.9rem;
transition: color 0.3s ease;
}
.footer-links a:hover {
color: #667eea !important;
}
.footer-text {
font-size: 0.8rem;
}
.footer-text a:hover {
color: #667eea !important;
}
.footer-text a {
text-decoration: underline;
}
.footer-text {
font-size: 0.8rem;
margin-bottom: 2rem;
}
.legal-content {
text-align: left;
line-height: 1.6;
}
.legal-content h2 {
color: #2c3e50;
margin-top: 2rem;
margin-bottom: 1rem;
}
.legal-content p {
margin-bottom: 1rem;
}
.legal-content ul {
margin-bottom: 1rem;
}
.main-container {
margin-bottom: 4rem;
}
</style>
{% block extra_css %}{% endblock %}
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}FragDenRat{% endblock %}</title>
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<link rel="icon" href="{% static 'favicon.svg' %}" type="image/svg+xml">
{% block extra_head %}{% endblock %}
<style>
body { background: #f8f9fa; }
.navbar-brand img { height: 36px; }
</style>
</head>
<body>
<div class="container">
<div class="main-container">
{% block content %}{% endblock %}
</div>
<!-- Footer -->
<footer class="footer mt-5 pt-4">
<div class="container">
<div class="row">
<div class="col-12 text-center">
<div class="footer-links mb-2">
<a href="/impressum" class="text-muted text-decoration-none me-3">Impressum</a>
<a href="/datenschutz" class="text-muted text-decoration-none me-3">Datenschutz</a>
<a href="https://git.project-insanity.org/onny/fragify" class="text-muted text-decoration-none" target="_blank">Source</a>
</div>
<div class="footer-text">
<small class="text-muted">
Projekt von <a href="https://project-insanity.org" class="text-muted text-decoration-none" target="_blank">Project-Insanity.org</a>,
follow us on <a href="https://social.project-insanity.org/@pi_crew" class="text-muted text-decoration-none" target="_blank">Mastodon</a> :)
</small>
</div>
</div>
</div>
</div>
</footer>
</div>
<nav class="navbar navbar-expand-lg bg-white border-bottom">
<div class="container">
<a class="navbar-brand d-flex align-items-center" href="/">
<img src="{% static 'favicon.svg' %}" alt="Logo" class="me-2"/>
<strong>FragDenRat</strong>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="mainNav">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" href="/">Start</a></li>
<li class="nav-item"><a class="nav-link" href="/gemeinden/">Gemeinden</a></li>
<li class="nav-item"><a class="nav-link" href="/mitglieder/">Stadträt:innen</a></li>
<li class="nav-item"><a class="nav-link" href="/parteien/">Parteien</a></li>
<li class="nav-item"><a class="nav-link" href="/fragen/">Fragen</a></li>
</ul>
</div>
</div>
</nav>
<script src="/static/js/bootstrap.bundle.min.js"></script>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/select2.min.js"></script>
{% block extra_js %}{% endblock %}
<main class="container py-4">
{% block content %}{% endblock %}
</main>
<script src="{% static 'js/bootstrap.bundle.min.js' %}"></script>
{% block extra_js %}{% endblock %}
</body>
</html>

View file

@ -0,0 +1,35 @@
{% extends "base.html" %}
{% block title %}Stadträt:innen FragDenRat{% endblock %}
{% block content %}
<h2 class="mb-3">Stadträt:innen</h2>
<div class="table-responsive">
<table class="table table-striped align-middle">
<thead>
<tr>
<th>Name</th>
<th>Gemeinde</th>
<th>Partei</th>
</tr>
</thead>
<tbody>
{% for m in items %}
<tr>
<td><a href="{% url 'member_detail' pk=m.id %}">{{ m.first_name }} {{ m.last_name }}</a></td>
<td>
{% if m.public_body %}
<a href="{% url 'public_body_detail' slug=m.public_body.slug %}">{{ m.public_body.name }}</a>
{% else %}{% endif %}
</td>
<td>
{% if m.party %}
<a href="{% url 'party_detail' pk=m.party.id %}">{{ m.party }}</a>
{% else %}{% endif %}
</td>
</tr>
{% empty %}
<tr><td colspan="3">Keine Einträge.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View file

@ -0,0 +1,20 @@
{% extends "base.html" %}
{% block title %}Parteien FragDenRat{% endblock %}
{% block content %}
<h2 class="mb-3">Parteien</h2>
<ul class="list-group">
{% for p in items %}
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>
<a href="{% url 'party_detail' pk=p.id %}" class="text-decoration-none">
<strong>{{ p.name }}</strong>
</a>
{% if p.abbreviation %}<span class="text-muted ms-2">({{ p.abbreviation }})</span>{% endif %}
</span>
{% if p.color %}<span class="badge" style="background-color: {{ p.color }}">&nbsp;&nbsp;&nbsp;</span>{% endif %}
</li>
{% empty %}
<li class="list-group-item">Keine Einträge.</li>
{% endfor %}
</ul>
{% endblock %}

View file

@ -0,0 +1,20 @@
{% extends "base.html" %}
{% block title %}Gemeinden FragDenRat{% endblock %}
{% block content %}
<h2 class="mb-3">Gemeinden</h2>
<ul class="list-group">
{% for g in items %}
<li class="list-group-item d-flex justify-content-between align-items-center">
<div>
<a href="{% url 'public_body_detail' slug=g.slug %}" class="text-decoration-none">
<strong>{{ g.name }}</strong>
</a>
{% if g.website %}<div><a href="{{ g.website }}" target="_blank" rel="noopener">{{ g.website }}</a></div>{% endif %}
</div>
<span class="badge bg-secondary">{{ g.members.count }} Mitglieder</span>
</li>
{% empty %}
<li class="list-group-item">Keine Einträge.</li>
{% endfor %}
</ul>
{% endblock %}

View file

@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block title %}Fragen FragDenRat{% endblock %}
{% block content %}
<h2 class="mb-3">Fragen</h2>
<ul class="list-group">
{% for q in items %}
<li class="list-group-item">
<strong>{{ q.title }}</strong>
<div class="text-muted">an {{ q.member }} • {{ q.created_at|date:'d.m.Y H:i' }}</div>
</li>
{% empty %}
<li class="list-group-item">Keine Einträge.</li>
{% endfor %}
</ul>
{% endblock %}

View file

@ -1,11 +1,11 @@
{% extends "base.html" %}
{% block title %}Datenschutz - Fragify{% endblock %}
{% block title %}Datenschutz - FragDenRat{% endblock %}
{% block content %}
<div class="text-center">
<h1 class="title display-4">
<a href="/" class="text-decoration-none text-dark">Fragify</a>
<a href="/" class="text-decoration-none text-dark">FragDenRat</a>
</h1>
<h2 class="text-muted h4 mt-3">Datenschutzerklärung</h2>
</div>
@ -59,7 +59,7 @@
<h3>Cookies</h3>
<p>
<strong>Diese Website verwendet keine Cookies.</strong> Es werden keine Tracking-Cookies oder Analyse-Cookies gesetzt. Die Website funktioniert vollständig ohne Cookie-Speicherung.
<strong>Diese Website verwendet keine Tracking-Cookies.</strong>
</p>
<h3>Server-Log-Dateien</h3>
@ -78,15 +78,7 @@
Eine Zusammenführung dieser Daten mit anderen Datenquellen wird nicht vorgenommen.
</p>
<h2>6. API-Nutzung</h2>
<p>
Diese Website nutzt die öffentliche API von FragDenStaat.de, um Behördeninformationen abzurufen. Bei der Nutzung der Suchfunktion werden Ihre Suchanfragen an die FragDenStaat.de API weitergeleitet. Es werden keine persönlichen Daten an FragDenStaat.de übertragen, außer den reinen Suchbegriffen.
</p>
<p>
<strong>Wichtig:</strong> Wir speichern keine Ihrer Suchanfragen oder generierten Links auf unseren Servern. Alle Daten werden nur temporär im Browser verarbeitet und nicht dauerhaft gespeichert.
</p>
<h2>7. Kontakt</h2>
<h2>6. Kontakt</h2>
<p>
Bei Fragen zur Erhebung, Verarbeitung oder Nutzung Ihrer personenbezogenen Daten wenden Sie sich bitte an:
</p>
@ -95,7 +87,7 @@
E-Mail: <a href="mailto:onny@project-insanity.org">onny@project-insanity.org</a>
</p>
<h2>8. Änderungen</h2>
<h2>7. Änderungen</h2>
<p>
Wir behalten uns vor, diese Datenschutzerklärung anzupassen, damit sie stets den aktuellen rechtlichen Anforderungen entspricht oder um Änderungen unserer Leistungen in der Datenschutzerklärung umzusetzen, z. B. bei der Einführung neuer Services.
</p>

View file

@ -1,11 +1,11 @@
{% extends "base.html" %}
{% block title %}Impressum - Fragify{% endblock %}
{% block title %}Impressum - FragDenRat{% endblock %}
{% block content %}
<div class="text-center">
<h1 class="title display-4">
<a href="/" class="text-decoration-none text-dark">Fragify</a>
<a href="/" class="text-decoration-none text-dark">FragDenRat</a>
</h1>
<h2 class="text-muted h4 mt-3">Impressum</h2>
</div>

View file

@ -1,126 +1,16 @@
{% extends "base.html" %}
{% block title %}Fragify{% endblock %}
{% block title %}FragDenRat{% endblock %}
{% block content %}
<div class="text-center">
<h1 class="title display-4">Fragify</h1>
<p class="description">
Erstelle einfach Links für Anfragen bei dem Portal
<a href="https://fragdenstaat.de" target="_blank" class="text-decoration-none">FragDenStaat.de</a>,
welche du vorausfüllen und an Freund:innen schicken kannst!
</p>
<form id="fragifyForm" class="text-start">
<div class="mb-4">
<label for="publicbody" class="form-label fw-bold">Behörde</label>
<select class="form-select" id="publicbody" name="publicbody_id" required>
<option value="">Behörde auswählen...</option>
</select>
</div>
<div class="mb-4">
<label for="subject" class="form-label fw-bold">Betreff</label>
<input type="text" class="form-control" id="subject" name="subject"
placeholder="Betreff der Anfrage" required>
</div>
<div class="mb-4">
<label for="body" class="form-label fw-bold">Dokumente anfragen:</label>
<textarea class="form-control" id="body" name="body" rows="5"
placeholder="Beschreibe hier, welche Dokumente oder Informationen du anfragen möchtest..." required></textarea>
</div>
<div class="text-center">
<button type="submit" class="btn btn-primary btn-lg">
<span class="btn-text">Anfrage Link generieren</span>
<span class="loading spinner-border spinner-border-sm ms-2" role="status"></span>
</button>
</div>
</form>
<div id="result" class="mt-4" style="display: none;">
<h5 class="text-success mb-3">Link erfolgreich generiert!</h5>
<div class="input-group">
<input type="text" class="form-control" id="generatedLinkInput" readonly>
<button class="btn btn-outline-primary" type="button" id="copyBtn">In Zwischenablage</button>
</div>
</div>
<div class="row align-items-center">
<div class="col-lg-8 mx-auto text-center">
<h1 class="display-5 fw-semibold mb-3">FragDenRat</h1>
<p class="lead text-muted">Stelle öffentlich Fragen an Stadträt:innen in deinen Gemeinderäten transparent, nachvollziehbar, gemeinsam.</p>
<div class="d-flex justify-content-center gap-2 mt-4">
<a class="btn btn-primary" href="/fragen/">Fragen entdecken</a>
<a class="btn btn-outline-secondary" href="/gemeinden/">Gemeinden durchsuchen</a>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
$(document).ready(function() {
// Initialize Select2 for public bodies
$('#publicbody').select2({
theme: 'bootstrap-5',
placeholder: 'Behörde auswählen...',
allowClear: true,
width: '100%',
ajax: {
url: '/api/publicbodies',
dataType: 'json',
delay: 250,
data: function(params) {
return {
search: params.term,
page: params.page || 1
};
},
processResults: function(data, params) {
params.page = params.page || 1;
return {
results: data.objects.map(function(item) {
return {
id: item.id,
text: item.name + ' (' + item.jurisdiction.name + ')'
};
}),
pagination: {
more: data.meta.next !== null
}
};
},
cache: true
}
});
// Handle form submission (client-side)
$('#fragifyForm').on('submit', function(e) {
e.preventDefault();
const publicbodyId = $('#publicbody').val() || '';
const subject = $('#subject').val() || '';
const body = $('#body').val() || '';
let link = 'https://fragdenstaat.de/anfrage-stellen/';
if (publicbodyId) {
link += `an/${publicbodyId}/`;
}
const params = new URLSearchParams();
if (subject) params.set('subject', subject);
if (body) params.set('body', body);
if ([...params].length > 0) {
link += `?${params.toString()}`;
}
$('#generatedLinkInput').val(link);
$('#result').show();
});
// Copy to clipboard
$(document).on('click', '#copyBtn', function() {
const link = $('#generatedLinkInput').val();
navigator.clipboard.writeText(link).then(function() {
const btn = $('#copyBtn');
const originalText = btn.text();
btn.text('Kopiert!');
setTimeout(() => btn.text(originalText), 1500);
});
});
});
</script>
{% endblock %}