Initial commit – AufmaßCreater v2.35
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="level">
|
||||
<div class="level-left">
|
||||
<h1 class="title is-3">Dashboard</h1>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
<div class="select is-small">
|
||||
<select id="profile-select" onchange="loadProfile(this.value)">
|
||||
{% for p in profile_list or [] %}
|
||||
<option value="{{ p.id }}" {{ 'selected' if active_profile and active_profile.id == p.id }}>{{ p.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-small is-info" onclick="saveProfile()">Speichern</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-small is-light" onclick="newProfile()">+ Neu</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="dashboard-cards" class="columns is-multiline mt-3">
|
||||
<div class="column is-one-third" data-card="projekte">
|
||||
<div class="box has-text-centered">
|
||||
<p class="heading">Projekte (aktiv)</p>
|
||||
<p class="title">{{ projekte_anzahl or 0 }}</p>
|
||||
<a href="{{ url_for('aufmass.index') }}" class="button is-small is-link is-outlined mt-2">Alle Projekte</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-one-third" data-card="module">
|
||||
<div class="box has-text-centered">
|
||||
<p class="heading">Module verfügbar</p>
|
||||
<p class="title">{{ modules|length }}</p>
|
||||
<a href="{{ url_for('admin.firma') }}" class="button is-small is-link is-outlined mt-2">Module verwalten</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-one-third" data-card="mitarbeiter">
|
||||
<div class="box has-text-centered">
|
||||
<p class="heading">Mitarbeiter</p>
|
||||
<p class="title">{{ mitarbeiter_anzahl }}</p>
|
||||
<a href="{{ url_for('admin.firma') }}" class="button is-small is-link is-outlined mt-2">Mitarbeiter verwalten</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-one-third" data-card="projektwerte">
|
||||
<div class="box">
|
||||
<p class="heading">Projektwerte (€)</p>
|
||||
{% if gesamt_summe > 0 %}
|
||||
<p class="is-size-4 has-text-weight-bold has-text-primary">Gesamt: {{ gesamt_summe|german_number }} €</p>
|
||||
<table class="table is-fullwidth is-hoverable mt-2" style="font-size:0.85rem">
|
||||
<thead><tr><th>Bezeichnung</th><th style="text-align:right">Wert (€)</th></tr></thead>
|
||||
<tbody>
|
||||
{% for p, summe in projekte_mit_summe %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('aufmass.aufmass_list', project_id=p.id) }}">{{ p.bezeichnung or p.sm_nr }}</a></td>
|
||||
<td style="text-align:right">{{ summe|german_number }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="has-text-grey is-size-7">Keine Projekte mit Werten.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box" data-card="lizenzen">
|
||||
<h2 class="title is-5">Lizenzen</h2>
|
||||
<div class="columns is-multiline">
|
||||
<div class="column is-one-third">
|
||||
<div class="notification is-light has-text-centered" style="margin-bottom:0">
|
||||
<p class="heading">Mitarbeiter</p>
|
||||
<p class="title is-4">{{ mitarbeiter_anzahl }}/{{ license_max_ma }}</p>
|
||||
<p class="is-size-7 has-text-grey">belegt/verfügbar</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-one-third">
|
||||
<div class="notification is-light has-text-centered" style="margin-bottom:0">
|
||||
<p class="heading">Module</p>
|
||||
<p class="title is-4">{{ license_module_used }}/{{ license_module_count }}</p>
|
||||
<p class="is-size-7 has-text-grey">belegt/verfügbar</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-one-third">
|
||||
<div class="notification is-light has-text-centered" style="margin-bottom:0">
|
||||
<p class="heading">Lizenzen</p>
|
||||
<p class="title is-4">{{ license_count }}</p>
|
||||
<p class="is-size-7 has-text-grey">vorhanden</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
|
||||
<script>
|
||||
var cardContainer = document.getElementById('dashboard-cards');
|
||||
|
||||
new Sortable(cardContainer, {
|
||||
animation: 150,
|
||||
handle: '.box',
|
||||
onEnd: function() {
|
||||
var order = [];
|
||||
cardContainer.querySelectorAll('.column[data-card]').forEach(function(col) {
|
||||
order.push(col.dataset.card);
|
||||
});
|
||||
localStorage.setItem('dash_order_' + {{ current_user.id }}, JSON.stringify(order));
|
||||
}
|
||||
});
|
||||
|
||||
function loadOrder() {
|
||||
var saved = localStorage.getItem('dash_order_' + {{ current_user.id }});
|
||||
if (!saved) return;
|
||||
var order = JSON.parse(saved);
|
||||
var cards = {};
|
||||
cardContainer.querySelectorAll('.column[data-card]').forEach(function(col) {
|
||||
cards[col.dataset.card] = col;
|
||||
});
|
||||
order.forEach(function(key) {
|
||||
if (cards[key]) cardContainer.appendChild(cards[key]);
|
||||
});
|
||||
}
|
||||
loadOrder();
|
||||
|
||||
function saveProfile() {
|
||||
var order = [];
|
||||
cardContainer.querySelectorAll('.column[data-card]').forEach(function(col) {
|
||||
order.push(col.dataset.card);
|
||||
});
|
||||
var lizOrder = document.querySelector('[data-card="lizenzen"]');
|
||||
var sel = document.getElementById('profile-select');
|
||||
var profileId = sel.value;
|
||||
fetch('/api/profiles/' + (profileId || ''), {
|
||||
method: profileId ? 'PUT' : 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
name: 'Dashboard',
|
||||
view_type: 'dashboard',
|
||||
config_json: {card_order: order}
|
||||
})
|
||||
}).then(function(r){return r.json()}).then(function(data){
|
||||
if (data.id) {
|
||||
var opt = sel.querySelector('option[value="'+profileId+'"]');
|
||||
if (!opt) {
|
||||
opt = document.createElement('option');
|
||||
opt.value = data.id;
|
||||
opt.textContent = data.name || 'Dashboard';
|
||||
sel.appendChild(opt);
|
||||
}
|
||||
sel.value = data.id;
|
||||
}
|
||||
alert('Gespeichert');
|
||||
});
|
||||
}
|
||||
|
||||
function loadProfile(profileId) {
|
||||
if (!profileId) return;
|
||||
fetch('/api/profiles/' + profileId)
|
||||
.then(function(r){return r.json()})
|
||||
.then(function(data){
|
||||
var cfg = data.config_json || {};
|
||||
if (cfg.card_order) {
|
||||
var cards = {};
|
||||
cardContainer.querySelectorAll('.column[data-card]').forEach(function(col) {
|
||||
cards[col.dataset.card] = col;
|
||||
});
|
||||
cfg.card_order.forEach(function(key) {
|
||||
if (cards[key]) cardContainer.appendChild(cards[key]);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function newProfile() {
|
||||
var name = prompt('Profil-Name:');
|
||||
if (!name) return;
|
||||
fetch('/api/profiles', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
name: name,
|
||||
view_type: 'dashboard',
|
||||
config_json: {card_order: []}
|
||||
})
|
||||
}).then(function(r){return r.json()}).then(function(data){
|
||||
if (data.id) {
|
||||
var sel = document.getElementById('profile-select');
|
||||
var opt = document.createElement('option');
|
||||
opt.value = data.id;
|
||||
opt.textContent = data.name;
|
||||
opt.selected = true;
|
||||
sel.appendChild(opt);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user