This commit is contained in:
relikd
2023-05-29 15:20:07 +02:00
commit 1380b156d8
126 changed files with 3612 additions and 0 deletions

82
app/base/templates/base.html Executable file
View File

@@ -0,0 +1,82 @@
{% load static %}
{% load field_utils %}
{% load breadcrumbs %}
{% load url_utils %}
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<link rel="shortcut icon" href="{% static 'img/favicon.ico' %}">
<link rel="stylesheet" type="text/css" href="{% static 'bootstrap4.6.2/css/bootstrap.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'fontawesome6.4.0/css/all.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}">
{% comment %} <script src="{% static 'jquery3.6.4/js/jquery.min.js' %}"></script> {% endcomment %}
{% comment %} <script src="{% static 'bootstrap4.6.2/js/bootstrap.min.js' %}"></script> {% endcomment %}
<script src="{% static 'js/main.js' %}"></script>
<script src="{% static 'js/onload.js' %}" defer></script>
<title>{% block title %}{{ title }}{% endblock title %}</title>
</head>
<body>
<header>
<nav id="main-nav" class="navbar navbar-expand-lg navbar-light">
<a id="navbar-toggler" onclick="toggleMenu('sidebar-wrapper')">
<span class="navbar-toggler-icon"></span>
</a>
<a class="navbar-brand d-none d-md-block" href="{% url 'index' %}">
<img id="logo" src="{% static 'img/logo.png' %}" alt="Werkstattmanagement">
</a>
<form id="global-search" action="{% url 'person:list' %}" method="get">
<input name="q" value="{{ request.GET.q }}">
<button type="submit" class="btn bg-owba"><i class="fas fa-search"></i></button>
</form>
<div class="avatar">
<button type="button" data-toggle="dropdown" onclick="toggleMenu('profileMenu')">
<i class="far fa-user fa-lg"></i>
</button>
<div id="profileMenu" class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="{% url 'logout' %}">Logout</a>
</div>
</div>
</nav>
</header>
<div class="d-flex" id="wrapper">
<div id="sidebar-wrapper" class="border-right bg-dim">
<div class="list-group list-group-flush">
<a href="{% url 'booking:list' %}" class="list-group-item list-group-item-action bg-dim"><i class="fas fa-clock"></i> Zeitbuchungen</a>
<a href="{% url 'person:list' %}" class="list-group-item list-group-item-action bg-dim"><i class="fas fa-users"></i> Werkstattnutzer:innen</a>
<a href="{% url 'course:list' %}" class="list-group-item list-group-item-action bg-dim"><i class="fas fa-graduation-cap"></i> Einweisungen</a>
<a href="{% url 'transaction:list' %}" class="list-group-item list-group-item-action bg-dim"><i class="fas fa-euro-sign"></i> Transaktionen</a>
<a href="{% url 'trait:list' %}" class="list-group-item list-group-item-action bg-dim"><i class="fas fa-tags"></i> Attribute</a>
<a href="{% url 'settings' %}" class="list-group-item list-group-item-action bg-dim"><i class="fas fa-sliders-h"></i> Einstellungen</a>
</div>
</div>
<div id="page-content-wrapper" class="container-fluid position-relative">
{% breadcrumbs %}
{% block page_title %}
{% include 'widgets/title-with-icon.html' with icon=icon title=title %}
{% endblock page_title %}
<div class="toolbar">
{% block toolbar_left %}
{% endblock toolbar_left %}
<span style="flex-grow: 1"></span>
{% block toolbar_right %}
{% for item in toolbar_buttons %}
<a href="{% url_builder item %}" class="btn btn-primary">
{% if item.icon %}
<i class="fas fa-{{ item.icon }}"></i>
{% endif %}
{{ item.label }}
</a>
{% endfor %}
{% endblock %}
</div>
<div id="main-content">
{% block content %}{% endblock content %}
</div>
</div>
</div>
{% block scripts %}
{% endblock scripts %}
</body>
</html>

View File

@@ -0,0 +1,11 @@
{% extends 'generic/list.html' %}
{% load tabular_list %}
{% block content %}
{% if open_bookings %}
<h2>Offene {{ title }}</h2>
{% tabular_list objects=open_bookings views=views columns=list_columns render_options=list_render %}
<h2>Alle {{ title }}</h2>
{% endif %}
{{ block.super }}
{% endblock content %}

View File

@@ -0,0 +1,7 @@
<div>
{% include "django/forms/widgets/date.html" %}
<button type="button" class="btn btn-outline-primary" onclick="dateTimeSetNow('{{ widget.attrs.id }}')" title="Aktuelles Datum setzen">Heute</button>
{% if not widget.required %}
<button type="button" class="btn btn-outline-secondary" onclick="dateTimeReset('{{ widget.attrs.id }}')" title="Datum zurücksetzen"><i class="far fa-trash-alt"></i></button>
{% endif %}
</div>

View File

@@ -0,0 +1,8 @@
<div>
<input type="date" id="{{ widget.attrs.id }}_day" name="{{ widget.name }}"{% if widget.value %} value="{{ widget.value|date:'Y-m-d' }}"{% endif %}>
<input type="time" id="{{ widget.attrs.id }}_time" name="{{ widget.name }}"{% if widget.value %} value="{{ widget.value|date:'H:i' }}"{% endif %}>
<button type="button" class="btn btn-outline-primary" onclick="dateTimeSetNow('{{ widget.attrs.id }}')" title="Aktuelles Datum setzen">Jetzt</button>
{% if not widget.required %}
<button type="button" class="btn btn-outline-secondary" onclick="dateTimeReset('{{ widget.attrs.id }}')" title="Datum zurücksetzen"><i class="far fa-trash-alt"></i></button>
{% endif %}
</div>

View File

@@ -0,0 +1,3 @@
{% load utils %}
<textarea name="{{ widget.name }}" rows="{{widget.value|count_lines|min:4|max:10}}"{% include "django/forms/widgets/attrs.html" %}>
{% if widget.value %}{{ widget.value }}{% endif %}</textarea>

View File

@@ -0,0 +1,13 @@
{% spaceless %}
{% load utils %}{# model_verbose_name #}
{% if render_options.verbose_name %}
{{ render_options.verbose_name }}
{% elif field.verbose_name %}
{{ field.verbose_name }}
{% elif field.one_to_one or field.many_to_one %}
{{ field.remote_field|model_verbose_name }}
{% else %}
{{ field }}
{% endif %}
{% endspaceless %}

View File

@@ -0,0 +1,14 @@
{% spaceless %}
{% load utils %}{# format #}
{% load field_utils %}{# ref_link #}
{% if render_options.date_format %}
{{ obj|date:render_options.date_format|default:'' }}
{% elif render_options.format %}
{{ obj|format:render_options.format }}
{% elif clickable_refs %}
{{ obj|ref_link:'reflink' }}
{% else %}
{{ obj }}
{% endif %}
{% endspaceless %}{% if render_options.is_price %}&nbsp;€{% endif %}

View File

@@ -0,0 +1,18 @@
{% extends 'base.html' %}
{% load static %}
{% load url_utils %}{# back_url #}
{% block content %}
<form method="post" class="create-form">{% csrf_token %}
{{ form.as_div }}
<a class="btn btn-secondary" href="{% back_url %}">Abbrechen</a>
<button type="submit" class="btn btn-primary">{{submit_label}}</button>
</form>
{% endblock %}
{% block scripts %}
<link rel="stylesheet" type="text/css" href="{% static 'select2-4.0.13/css/select2.min.css' %}">
<script src="{% static 'jquery3.6.4/js/jquery.min.js' %}"></script>
<script src="{% static 'select2-4.0.13/js/select2.min.js' %}"></script>
<script src="{% static 'js/forms.js' %}"></script>
{% endblock %}

View File

@@ -0,0 +1,10 @@
{% extends 'base.html' %}
{% load url_utils %}
{% block content %}
<form method="post">{% csrf_token %}
<p>Bist du dir sicher dass du {{ model_verbose_name }} <b>"{{ object }}"</b> löschen möchtest?</p>
<a class="btn btn-primary" href="{% back_url %}">Abbrechen</a>
<button type="submit" class="btn btn-danger"><i class="far fa-trash-alt"></i> {{submit_label|default:'Weg damit!'}}</button>
</form>
{% endblock %}

View File

@@ -0,0 +1,17 @@
{% extends 'base.html' %}
{% load url_utils %}
{% block toolbar_left %}
{% if views.update %}
<a class="btn btn-sm btn-secondary" href="{% url views.update object.pk %}{% query_url prev=request.path %}">
<i class="fas fa-edit"></i> Bearbeiten</a>
{% endif %}
{% if views.delete %}
<a class="btn btn-sm btn-secondary" href="{% url views.delete object.pk %}{% query_url prev=request.path %}">
<i class="fas fa-trash-alt"></i> Löschen</a>
{% endif %}
{% endblock %}
{% block content %}
{% include 'templatetags/detail_table.html' %}
{% endblock %}

View File

@@ -0,0 +1,44 @@
{% extends 'base.html' %}
{% load tabular_list %}
{% load url_utils %}
{% load utils %}
{% block title %}
{% if active_search_query %}Suchergebnisse {% endif %}{{ block.super }}
{% endblock %}
{% block page_title %}
{% if active_search_query %}
{% include 'widgets/title-with-icon.html' with icon='search' title=active_search_query|format:'Suchergebnisse für "{}"' %}
{% endif %}
{{ block.super }}
{% endblock %}
{% block toolbar_right %}
{% if views.create %}
<a class="btn btn-primary" href="{% url views.create %}">
<i class="fas fa-plus"></i> {{ model_verbose_name }} hinzufügen</a>
{% endif %}
{% endblock %}
{% block content %}
{% include 'widgets/table_filter.html' %}
{% tabular_list objects=object_list views=views columns=list_columns render_options=list_render %}
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="{% query_url page=None %}">&laquo; Erste Seite</a> |
<a href="{% query_url page=page_obj.previous_page_number %}">vorherige Seite</a>
{% endif %}
<span class="current">
Seite {{ page_obj.number|default:1 }} von {{ page_obj.paginator.num_pages|default:1 }}.
</span>
{% if page_obj.has_next %}
<a href="{% query_url page=page_obj.next_page_number %}">nächste Seite</a> |
<a href="{% query_url page=page_obj.paginator.num_pages %}">Letzte Seite &raquo;</a>
{% endif %}
</span>
</div>
{% endblock %}

View File

@@ -0,0 +1,18 @@
{% extends 'base.html' %}
{% load static %}
{% load url_utils %}{# back_url #}
{% block content %}
<form method="post" class="create-form">{% csrf_token %}
{{ form.as_div }}
<a class="btn btn-secondary" href="{% back_url %}">Abbrechen</a>
<button type="submit" class="btn btn-primary">Änderungen speichern</button>
</form>
{% endblock %}
{% block scripts %}
<link rel="stylesheet" type="text/css" href="{% static 'select2-4.0.13/css/select2.min.css' %}">
<script src="{% static 'jquery3.6.4/js/jquery.min.js' %}"></script>
<script src="{% static 'select2-4.0.13/js/select2.min.js' %}"></script>
<script src="{% static 'js/forms.js' %}"></script>
{% endblock %}

View File

@@ -0,0 +1,89 @@
{% extends 'generic/detail.html' %}
{% load static %}
{% load tabular_list %}
{% load utils %}
{% load url_utils %}
{% load color_price %}
{% block toolbar_left %}
{% endblock %}
{% block content %}
{% query_url prev=request.path prevname=object|truncatechars:40 as back_query %}
<div class="container-fluid">
<div class="row">
<div class="col-md-8 p-0">
<div class="mb-3">
<h6 class="d-inline-block">Attribute:</h6>
{% for attr in object.attributes|dictsort:2 %}
<a class="badge border1" href="{% url 'trait-mapping:detail' attr.0 %}">{{ attr.2 }}</a>
{% endfor %}
<a class="badge badge-secondary" href="{% url 'trait-mapping:create' %}{{ back_query }}&user={{ object.pk }}" title="Attribut hinzufügen"><i class="fas fa-plus"></i></a>
</div>
{% include 'widgets/alerts.html' %}
{% include 'widgets/note-modal.html' %}
<div class="h-with-action">
<h2>Stammdaten:</h2>
<div>
<a title="{{model_verbose_name}} bearbeiten" class="btn btn-sm btn-outline-primary" href="{% url 'person:update' object.pk %}{{ back_query }}"><i class="fas fa-edit"></i></a>
<a title="{{model_verbose_name}} löschen" class="btn btn-sm btn-outline-danger" href="{% url 'person:delete' object.pk %}{{ back_query }}"><i class="far fa-trash-alt"></i></a>
<a title="Notiz hinzufügen" class="btn btn-sm btn-outline-warning" onclick="showNoteModal(true)"><i class="far fa-message"></i> Notiz</a>
</div>
</div>
{{ block.super }}
<div class="h-with-action">
<h2>Einweisungen:</h2>
<a class="btn btn-sm btn-outline-primary" href="{% url 'course-visit:create' %}{{ back_query }}&user={{ object.pk }}" title="Neue Einweisung hinzufügen"><i class="fas fa-plus"></i></a>
</div>
{% tabular_list objects=course_list.objects views=course_list.views columns=course_list.columns render_options=course_list.render %}
<div class="h-with-action">
<h2>Buchungen:</h2>
<a class="btn btn-sm btn-outline-primary" href="{% url 'booking:create' %}{{ back_query }}&user={{ object.pk }}" title="Neue Buchung hinzufügen"><i class="fas fa-plus"></i></a>
</div>
{% tabular_list objects=bookings.objects views=bookings.views columns=bookings.columns render_options=bookings.render %}
</div>
<div id="sidebar-detail" class="col-md-4">
{% include 'widgets/checkin-status.html' with user=object %}
<div class="text-center">
<span class="font-dosis">Kontostand</span>
<div class="account-balance mb-3 {% color_price object.account.balance %}">
{{ object.account.balance }}€
</div>
<form method="get" action="{% url 'transaction:create' %}" onsubmit>
<div class="input-group">
<input name="amount" class="form-control text-right" type="number" min="0" step="0.5" value="{{ object.account.balance|invert|min:object.account.balance|default:10|floatformat:'2u' }}">
<div class="input-group-append">
<span class="input-group-text"></span>
</div>
</div>
<input type="hidden" name="account" value="{{ object.account.pk }}">
<div class="btn-group btn-block mt-2">
<button name="_type" class="btn btn-outline-success" value="deposit-plus">Einzahlen</button>
<button name="_type" class="btn btn-outline-danger" value="deposit-minus">Auszahlen</button>
</div>
</form>
</div>
<div>
<span class="font-dosis text-center">Transaktionen:</span>
{% tabular_list objects=transaction_list.objects views=transaction_list.views columns=transaction_list.columns render_options=transaction_list.render is_small=True hide_thead=True %}
{% if transaction_list.objects|length > 0 %}
<div class="text-center">
<a href="{% url 'transaction:list' %}{{ back_query }}&user={{ object.pk }}">Alle anzeigen</a>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock content %}
{% block scripts %}
<script src="{% static 'js/person-detail.js' %}"></script>
{% endblock scripts %}

View File

@@ -0,0 +1,56 @@
{% load static %}
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<link rel="shortcut icon" href="{% static 'img/favicon.ico' %}">
<link rel="stylesheet" type="text/css" href="{% static 'bootstrap4.6.2/css/bootstrap.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/login.css' %}">
<title>Login</title>
</head>
<body>
<div class="login-form w-md-50">
{% if form.errors %}
<div class="alert alert-warning alert-dismissible fade show" role="alert">
Your username and password didn't match. Please try again.
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
{% endif %}
{% if next %}
{% if user.is_authenticated %}
<div class="alert alert-warning alert-dismissible fade show" role="alert">
Your account doesn't have access to this page. To proceed,
please login with an account that has access.
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
{% else %}
<p>Log dich ein um auf die Seite zuzugreifen.</p>
{% endif %}
{% endif %}
<h1>Bitte log dich ein.</h1>
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<p>
{{ form.username.label_tag }}
{{ form.username }}
</p>
<p>
{{ form.password.label_tag }}
{{ form.password }}
</p>
<input type="submit" class="btn btn-primary" value="Einloggen">
{% if next %}
<input type="hidden" name="next" value="{{ next }}">
{% else %}
<input type="hidden" name="next" value="/">
{% endif %}
</form>
</div>
</body>

View File

@@ -0,0 +1,8 @@
{% extends 'base.html' %}
{% load tabular_list %}
{% block content %}
<h2>Buchungstypen</h2>
{% tabular_list objects=booking_types.objects views=booking_types.views columns=booking_types.columns render_options=booking_types.render %}
{% endblock content %}

View File

@@ -0,0 +1,12 @@
<div id="breadcrumbs">
<a href="{% url 'index' %}">Home</a>
{% if breadcrumbs %}
{% for key, url in breadcrumbs %}
{% if url is None %}
&rsaquo; <span>{{ key }}</span>
{% else %}
&rsaquo; <a href="{{ url }}">{{ key }}</a>
{% endif %}
{% endfor %}
{% endif %}
</div>

View File

@@ -0,0 +1,15 @@
{% load field_utils %}{# get_fields, field_value #}
{% load utils %}{# get_item #}
<table class="table detail-table">
<tbody>
{% for field in object|get_fields:detail_fields %}
<tr>
{% with RENDER=detail_render|get_item:field.name %}
<th scope="row">{% include 'fragment/field_title.html' with field=field render_options=RENDER %}</th>
<td{% if field.get_internal_type == 'TextField' %} class="raw-text"{%endif%}>{% include 'fragment/field_value.html' with obj=object|field_value:field.name render_options=RENDER clickable_refs=True %}</td>
{% endwith %}
</tr>
{% endfor %}
</tbody>
</table>

View File

@@ -0,0 +1,76 @@
{% load utils %}{# format #}
{% load field_utils %}{# field_value, col_render_width, col_render_class #}
{% load url_utils %}{# query_url #}
{% if objects|length > 0 %}
<table class="table{% if is_small %} table-sm{% endif %}" {% if views.detail %}data-onclick="window.location.href='{% url views.detail 0 %}'.replace('0', row.dataset.pk)"{% endif %}>
<colgroup>
{% for column in columns %}
<col {% if column.render_options.width %}style="width: {{ column.render_options|col_render_width }}"{% endif %}>
{% endfor %}
{% if views.update or views.delete %}
<col style="width: 3.5rem">
{% endif %}
</colgroup>
{% if not hide_thead %}
<thead>
<tr>
{% for column in columns %}
<th scope="col"{% if column.render_options.class %} class="{{column.render_options.class}}"{% endif %}>
{% include 'fragment/field_title.html' with field=column.field render_options=column.render_options %}
</th>
{% endfor %}
{% if views.update or views.delete %}
<th scope="col"></th>
{% endif %}
</tr>
</thead>
{% endif %}
<tbody>
{% for object in objects %}
<tr data-pk="{{ object.pk }}">
{% for column in columns %}
{% with X=object|field_value:column.name %}
<td class="{{ X|col_render_class:column.render_options }}">
{% include 'fragment/field_value.html' with obj=X render_options=column.render_options %}
</td>
{% endwith %}
{% endfor %}
{% if views.update or views.delete %}
<td class="table-actions">{% spaceless %}
{% if views.update %}
<a title="Bearbeiten" href="{% url views.update object.pk %}{% query_url prev=request.path prevname=prevname %}">
<i class="far fa-edit"></i>
</a>
{% endif %}
{% if views.delete %}
<a title="Löschen" href="{% url views.delete object.pk %}{% query_url prev=request.path prevname=prevname %}">
<i class="far fa-trash-alt"></i>
</a>
{% endif %}
{% endspaceless %}</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
{% if request.GET.selected %}
<script>
highlight(document.querySelector('[data-pk="{{request.GET.selected}}"]'));
</script>
{% endif %}
{% else %}
<table class="table{% if is_small %} table-sm{% endif %}">
<tbody>
<tr class="text-center">
<td>
{% if active_search_query %}
Keine Einträge gefunden für "{{active_search_query}}".
{% else %}
Bisher keine Einträge.
{% endif %}
</td>
</tr>
</tbody>
</table>
{% endif %}

View File

@@ -0,0 +1,27 @@
{% if object.note %}
<div class="alert alert-warning d-flex">
<span><i class="far fa-message"></i></span>
<span class="raw-text ml-2">{{ object.note }}</span>
</div>
{% endif %}
{% if object.agreed_to_terms_of_service is False %}
<div class="alert alert-warning">
<span>Hat noch nicht in die Nutzungsbedingungen eingewilligt!</span>
</div>
{% endif %}
{% if object.identified is False %}
<div class="alert alert-warning">
<span>Hat noch kein Ausweisdokument vorgelegt!</span>
</div>
{% endif %}
{% if object.missing_courses|length > 0 %}
<div class="alert alert-warning">
<span>Es fehlen noch folgende Einweisungen:</span>
<hr>
<ul>
{% for course in object.missing_courses %}
<li>{{ course.title }} <a href="{% url 'course-visit:create' %}?user={{ object.pk }}&course={{ course.pk }}&prev={{request.path}}">(hinzufügen)</a></li>
{% endfor %}
</ul>
</div>
{% endif %}

View File

@@ -0,0 +1,22 @@
{% load field_utils %}
<div class="text-center">
{% if user.current_checkin %}
<div id="checkin-timer" class="text-green mb-3" data-since="{{ user.current_checkin.begin_time|date:'r' }}">0h 0min 0s</div>
<div class="checkin-title mb-3"><i class="fas fa-circle text-green"></i> {{ user.first_name }} ist in der Werkstatt.</div>
<a class="btn btn-primary btn-block" href="{% url 'toggle-checkin' user.id %}?next={{ request.path }}">{{ user.first_name }} auschecken</a>
{% else %}
<div class="checkin-title mb-3"><i class="fas fa-circle text-red"></i> {{ user.first_name }} ist zuhause.</div>
<form method="GET" action="{% url 'toggle-checkin' user.id %}">
<div class="form-group">
<label for="plan-select">Tarif:</label>
<select class="custom-select mb-2" id="plan-select" name="plan">
<option value="basic" selected>BASIC</option>
<option value="plus">PLUS</option>
</select>
<input type="hidden" name="next" value="{{ request.path }}">
</div>
<button type="submit" class="btn btn-primary btn-block">{{ user.first_name }} einchecken</button>
</form>
{% endif %}
</div>

View File

@@ -0,0 +1,16 @@
<div id="note-modal" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST" action="{% url 'update-note' object.pk %}">
{% csrf_token %}
<div class="modal-body">
<textarea name="text" rows="4" class="form-control" placeholder="(keine Notiz)" autocomplete="off">{{ object.note }}</textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="showNoteModal(false)">Abbrechen</button>
<button type="submit" class="btn btn-primary">Notiz speichern</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,13 @@
{% load utils %}
{% load url_utils %}
{% if list_filter.user and request.GET.user %}
{% lookup_user request.GET.user as usr %}
{% elif list_filter.teacher and request.GET.teacher %}
{% lookup_user request.GET.teacher as usr %}
{% endif %}
{% if usr %}
<h4>Filter: <a href="{{ usr.get_absolute_url }}">{{ usr }}</a></h4>
Filter aufheben: <a href=".">alle anzeigen</a>
{% endif %}

View File

@@ -0,0 +1,4 @@
<div class="h1-w-icon">
<i class="fas fa-{{ icon }}"></i>
<h1>{{ title }}</h1>
</div>