371 lines
13 KiB
HTML
371 lines
13 KiB
HTML
{% load static tailwind_tags %}
|
|
|
|
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
<title>
|
|
{% block title %}
|
|
Projektverwaltung
|
|
{% endblock %}
|
|
</title>
|
|
<script>
|
|
if (localStorage.getItem("theme") === "dark") {
|
|
document.documentElement.classList.add("dark");
|
|
}
|
|
</script>
|
|
{% tailwind_css %}
|
|
|
|
{% if company_settings.favicon %}
|
|
<link rel="icon" type="image/png" href="{{ company_settings.favicon.url }}">
|
|
{% else %}
|
|
<link rel="icon" type="image/png" href="{% static 'favicon.png' %}">
|
|
{% endif %}
|
|
|
|
<style>
|
|
:root {
|
|
--company-color: {{ company_settings.color }};
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="min-h-screen bg-slate-100 dark:bg-slate-900 text-slate-900 dark:text-white transition-colors">
|
|
<div class="flex min-h-screen">
|
|
|
|
{% if user.is_authenticated %}
|
|
<div id="overlay"
|
|
class="fixed inset-0 bg-black/40 hidden z-30 lg:hidden"
|
|
onclick="toggleSidebar()"></div>
|
|
|
|
<!-- Sidebar -->
|
|
<aside id="sidebar" class="fixed top-0 z-40 w-72 h-screen bg-slate-800 text-white transform -translate-x-full lg:translate-x-0 transition-transform duration-300 flex flex-col border-r border-white/10">
|
|
|
|
<div class="px-5 py-5 border-b border-white/10">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-2 h-2 rounded-full flex-shrink-0" style="background:var(--company-color);"></div>
|
|
<div>
|
|
<p class="text-sm font-semibold text-white leading-none">
|
|
{{ company_settings.companyname|default:"Projektverwaltung" }}
|
|
</p>
|
|
<p class="text-xs mt-1" style="color:rgba(255,255,255,.3);">Projektverwaltung</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<nav class="flex-1 p-4 space-y-2">
|
|
<div class="w-full rounded-lg flex items-center justify-between px-4 py-3 hover:bg-slate-700">
|
|
<a href="{% url 'core:dashboard' %}">
|
|
Dashboard
|
|
</a>
|
|
</div>
|
|
|
|
<div class="space-y-2">
|
|
|
|
<!-- Projekt wechseln -->
|
|
<div class="rounded-lg overflow-hidden">
|
|
|
|
<button
|
|
onclick="toggleProjects()"
|
|
class="w-full flex items-center justify-between px-4 py-3 hover:bg-slate-700">
|
|
|
|
<span>Projekte</span>
|
|
|
|
<svg id="projectArrow"
|
|
class="w-4 h-4 transition-transform"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24">
|
|
<path stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M19 9l-7 7-7-7"/>
|
|
</svg>
|
|
|
|
</button>
|
|
|
|
<div id="projectMenu" class="hidden border-t border-slate-700">
|
|
|
|
{% for p in user_projects %}
|
|
<a href="{% url 'projects:overview' p.id %}"
|
|
class="block px-8 py-3 hover:bg-slate-700">
|
|
{{ p.name }}
|
|
</a>
|
|
{% endfor %}
|
|
|
|
{# Trennlinie #}
|
|
<div class="mx-4 my-1 border-t border-slate-700/60"></div>
|
|
|
|
<a href="{% url 'projects:add' %}"
|
|
class="block px-8 py-3 hover:bg-slate-700 text-slate-400 hover:text-white transition-colors">
|
|
Hinzufügen
|
|
</a>
|
|
|
|
{% if archived_count %}
|
|
<a href="{% url 'projects:archived' %}"
|
|
class="block px-8 py-3 hover:bg-slate-700 text-slate-400 hover:text-white transition-colors">
|
|
Archiviert ({{ archived_count }})
|
|
</a>
|
|
{% endif %}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- Rechnungswesen -->
|
|
<div class="rounded-lg overflow-hidden">
|
|
|
|
<button
|
|
type="button"
|
|
onclick="toggleInvoicesMenu()"
|
|
class="w-full flex items-center justify-between px-4 py-3 hover:bg-slate-700 transition">
|
|
|
|
<span>
|
|
Rechnungswesen
|
|
</span>
|
|
|
|
<svg id="invoiceArrow"
|
|
class="w-4 h-4 transition-transform duration-200"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24">
|
|
<path stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M19 9l-7 7-7-7"/>
|
|
</svg>
|
|
|
|
</button>
|
|
|
|
<div id="invoiceMenu" class="hidden border-t border-slate-700">
|
|
|
|
<a href="{% url 'invoices:overview' %}"
|
|
class="block px-8 py-3 hover:bg-slate-700 transition">
|
|
Übersicht
|
|
</a>
|
|
|
|
<a href="{% url 'invoices:invoices_offers_overview' %}"
|
|
class="block px-8 py-3 hover:bg-slate-700 transition">
|
|
Angebote
|
|
</a>
|
|
|
|
<a href="{% url 'invoices:invoices_invoices_overview' %}"
|
|
class="block px-8 py-3 hover:bg-slate-700 transition">
|
|
Rechnungen
|
|
</a>
|
|
|
|
<a href="{% url 'invoices:invoices_reminders_overview' %}"
|
|
class="block px-8 py-3 hover:bg-slate-700 transition">
|
|
Mahnungen
|
|
</a>
|
|
|
|
<a href="{% url 'invoices:customers_overview' %}"
|
|
class="block px-8 py-3 hover:bg-slate-700 transition">
|
|
Kunden
|
|
</a>
|
|
|
|
<a href="{% url 'invoices:items_overview' %}"
|
|
class="block px-8 py-3 hover:bg-slate-700 transition">
|
|
Posten
|
|
</a>
|
|
|
|
<a href="{% url 'invoices:invoices_payments_overview' %}"
|
|
class="block px-8 py-3 hover:bg-slate-700 transition">
|
|
Zahlungen
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
<!--
|
|
<div class="w-full rounded-lg flex items-center justify-between px-4 py-3 hover:bg-slate-700">
|
|
<a href="{% url 'core:dashboard' %}">
|
|
Finanzübersicht
|
|
</a>
|
|
</div>
|
|
<div class="w-full rounded-lg flex items-center justify-between px-4 py-3 hover:bg-slate-700">
|
|
<a href="{% url 'core:dashboard' %}">
|
|
Arbeitszeiten
|
|
</a>
|
|
</div>
|
|
-->
|
|
<div class="w-full rounded-lg flex items-center justify-between px-4 py-3 hover:bg-slate-700">
|
|
<a href="{% url 'inventory_global:overview' %}">
|
|
Inventar
|
|
</a>
|
|
</div>
|
|
<!--
|
|
<div class="w-full rounded-lg flex items-center justify-between px-4 py-3 hover:bg-slate-700">
|
|
<a href="{% url 'core:dashboard' %}">
|
|
Investitionen
|
|
</a>
|
|
</div>
|
|
-->
|
|
<div class="w-full rounded-lg flex items-center justify-between px-4 py-3 hover:bg-slate-700">
|
|
<a href="{% url 'accounts:overview' %}">
|
|
Mitarbeiter
|
|
</a>
|
|
</div>
|
|
|
|
<div class="w-full rounded-lg flex items-center justify-between px-4 py-3 hover:bg-slate-700">
|
|
<a href="{% url 'masterdata:overview' %}">
|
|
Einstellungen
|
|
</a>
|
|
</div>
|
|
|
|
</nav>
|
|
|
|
<div class="p-4 border-t border-white/10">
|
|
{% if user.is_superuser %}
|
|
<div class="w-full rounded-xl flex items-center justify-between px-4 py-3 hover:bg-slate-700 mb-4 border-white/10 border">
|
|
<a href="{% url 'admin:index' %}">Admin</a>
|
|
</div>
|
|
{% endif %}
|
|
<div class="flex gap-2 w-full">
|
|
<button id="theme-toggle" class="rounded-xl px-4 py-3 hover:bg-slate-700 border-white/10 border">🌙</button>
|
|
<form method="post" action="{% url 'accounts:logout' %}" class="w-full">
|
|
{% csrf_token %}
|
|
<button class="w-full rounded-xl px-4 py-3 border-white/10 border" style="background-color: {{ company_settings.color }};">Abmelden</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
</aside>
|
|
{% endif %}
|
|
|
|
<!-- Hauptbereich -->
|
|
{% if user.is_authenticated %}
|
|
<div class="flex-1 flex flex-col lg:ml-72">
|
|
{% else %}
|
|
<div class="flex-1 flex flex-col">
|
|
{% endif %}
|
|
<header class="lg:hidden bg-white shadow h-16 flex items-center px-4 dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
|
|
<button onclick="toggleSidebar()" class="text-3xl">☰</button>
|
|
<span class="ml-4 font-bold">{{ company_settings.companyname|default:"Projektverwaltung" }}</span>
|
|
</header>
|
|
<main class="flex-1 p-6 overflow-x-auto w-screen lg:w-auto">
|
|
{% block header %}
|
|
<div class="border border-slate-100 dark:border-slate-700 p-6 rounded-xl shadow mb-8 bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
|
|
<h1 class="text-xl font-bold">
|
|
{% block header_title %}{% endblock %}
|
|
</h1>
|
|
<p class="text-slate-400 mt-1 text-md">
|
|
{% block header_description %}{% endblock %}
|
|
</p>
|
|
</div>
|
|
{% endblock %}
|
|
{% block content %}
|
|
{% endblock %}
|
|
</main>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
{% if messages %}
|
|
<div class="fixed top-5 right-5 z-50 space-y-3">
|
|
{% for message in messages %}
|
|
<div
|
|
class="rounded-xl shadow-lg px-5 py-4 flex items-center gap-4
|
|
{% if message.tags == 'success' %}
|
|
bg-green-100 border border-green-300 text-green-800
|
|
{% elif message.tags == 'error' %}
|
|
bg-red-100 border border-red-300 text-red-800
|
|
{% elif message.tags == 'warning' %}
|
|
bg-yellow-100 border border-yellow-300 text-yellow-800
|
|
{% else %}
|
|
bg-blue-100 border border-blue-300 text-blue-800
|
|
{% endif %}
|
|
"
|
|
>
|
|
<div>
|
|
{{ message }}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
<script>
|
|
setTimeout(() => {
|
|
document
|
|
.querySelectorAll(".fixed.top-5.right-5 > div")
|
|
.forEach(el => {
|
|
|
|
el.style.opacity = "0";
|
|
|
|
setTimeout(() => {
|
|
el.remove();
|
|
}, 500);
|
|
|
|
});
|
|
|
|
}, 3000);
|
|
</script>
|
|
{% endif %}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
function toggleProjects() {
|
|
|
|
const menu = document.getElementById("projectMenu");
|
|
const arrow = document.getElementById("projectArrow");
|
|
|
|
menu.classList.toggle("hidden");
|
|
arrow.classList.toggle("rotate-180");
|
|
|
|
}
|
|
|
|
function toggleInvoicesMenu() {
|
|
|
|
const menu = document.getElementById("invoiceMenu");
|
|
const arrow = document.getElementById("invoiceArrow");
|
|
|
|
menu.classList.toggle("hidden");
|
|
arrow.classList.toggle("rotate-180");
|
|
|
|
}
|
|
|
|
function toggleSidebar() {
|
|
|
|
const sidebar = document.getElementById("sidebar");
|
|
const overlay = document.getElementById("overlay");
|
|
|
|
sidebar.classList.toggle("-translate-x-full");
|
|
overlay.classList.toggle("hidden");
|
|
|
|
}
|
|
</script>
|
|
|
|
<script>
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
const btn = document.getElementById("theme-toggle");
|
|
const html = document.documentElement;
|
|
|
|
// Gespeichertes Theme anwenden
|
|
const saved = localStorage.getItem("theme");
|
|
if (saved === "dark") {
|
|
html.classList.add("dark");
|
|
} else {
|
|
html.classList.remove("dark");
|
|
}
|
|
|
|
// Toggle
|
|
btn.addEventListener("click", () => {
|
|
html.classList.toggle("dark");
|
|
localStorage.setItem("theme", html.classList.contains("dark") ? "dark" : "light");
|
|
btn.textContent = html.classList.contains("dark") ? "☀️" : "🌙";
|
|
});
|
|
|
|
// Icon initial setzen
|
|
btn.textContent = html.classList.contains("dark") ? "☀️" : "🌙";
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |