Promote develop → main (2026-07-25 22:22 UTC) #16
@@ -118,7 +118,6 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Slug</th>
|
|
||||||
<th>Nutzer</th>
|
<th>Nutzer</th>
|
||||||
<th>Lizenz</th>
|
<th>Lizenz</th>
|
||||||
<th>Kontingent</th>
|
<th>Kontingent</th>
|
||||||
@@ -523,10 +522,7 @@
|
|||||||
<label for="newOrgName">Name</label>
|
<label for="newOrgName">Name</label>
|
||||||
<input type="text" id="newOrgName" required>
|
<input type="text" id="newOrgName" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<input type="hidden" id="newOrgSlug">
|
||||||
<label for="newOrgSlug">Slug (URL-freundlich)</label>
|
|
||||||
<input type="text" id="newOrgSlug" required pattern="[a-z0-9-]+" placeholder="z.B. bundespolizei">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="newOrgLanguage">Pipeline-Sprache</label>
|
<label for="newOrgLanguage">Pipeline-Sprache</label>
|
||||||
<select id="newOrgLanguage">
|
<select id="newOrgLanguage">
|
||||||
@@ -1109,7 +1105,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/a11y.js?v=20260725a"></script>
|
<script src="/static/js/a11y.js?v=20260725a"></script>
|
||||||
<script src="/static/js/app.js?v=20260725q"></script>
|
<script src="/static/js/app.js?v=20260725r"></script>
|
||||||
<script src="/static/js/sources.js?v=20260522x2"></script>
|
<script src="/static/js/sources.js?v=20260522x2"></script>
|
||||||
<script src="/static/js/x-scraper.js?v=20260522a"></script>
|
<script src="/static/js/x-scraper.js?v=20260522a"></script>
|
||||||
<script src="/static/js/source-health.js?v=20260509l"></script>
|
<script src="/static/js/source-health.js?v=20260509l"></script>
|
||||||
|
|||||||
@@ -253,13 +253,12 @@ function orgUsageCell(orgId) {
|
|||||||
function renderOrgTable(orgs) {
|
function renderOrgTable(orgs) {
|
||||||
const tbody = document.getElementById("orgTable");
|
const tbody = document.getElementById("orgTable");
|
||||||
if (orgs.length === 0) {
|
if (orgs.length === 0) {
|
||||||
tbody.innerHTML = '<tr><td colspan="7" class="text-muted">Keine Organisationen</td></tr>';
|
tbody.innerHTML = '<tr><td colspan="6" class="text-muted">Keine Organisationen</td></tr>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tbody.innerHTML = orgs.map(o => `
|
tbody.innerHTML = orgs.map(o => `
|
||||||
<tr class="org-row" onclick="openOrg(${o.id})" title="Organisation öffnen">
|
<tr class="org-row" onclick="openOrg(${o.id})" title="Organisation öffnen">
|
||||||
<td><a href="#" class="text-accent" style="text-decoration: none;" onclick="openOrg(${o.id}); return false;">${esc(o.name)}</a></td>
|
<td><a href="#" class="text-accent" style="text-decoration: none;" onclick="openOrg(${o.id}); return false;">${esc(o.name)}</a></td>
|
||||||
<td class="text-secondary">${esc(o.slug)}</td>
|
|
||||||
<td>${o.user_count}</td>
|
<td>${o.user_count}</td>
|
||||||
<td><span class="badge badge-${o.license_type || 'none'}">${o.license_type || "Keine"}</span></td>
|
<td><span class="badge badge-${o.license_type || 'none'}">${o.license_type || "Keine"}</span></td>
|
||||||
<td>${orgUsageCell(o.id)}</td>
|
<td>${orgUsageCell(o.id)}</td>
|
||||||
@@ -289,8 +288,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
let currentOrgData = null; // zuletzt geladene Org (fuer Inline-Einstellungen)
|
let currentOrgData = null; // zuletzt geladene Org (fuer Inline-Einstellungen)
|
||||||
|
|
||||||
function renderOrgHeader(org) {
|
function renderOrgHeader(org) {
|
||||||
const kpiAlt = document.getElementById("orgKpiCredits");
|
|
||||||
const kpiText = kpiAlt ? kpiAlt.textContent : "wird geladen …";
|
|
||||||
document.getElementById("orgDetailHeader").innerHTML = `
|
document.getElementById("orgDetailHeader").innerHTML = `
|
||||||
<div style="display:flex; align-items:flex-start; gap:10px; flex-wrap:wrap;">
|
<div style="display:flex; align-items:flex-start; gap:10px; flex-wrap:wrap;">
|
||||||
<div style="flex:1; min-width:260px;">
|
<div style="flex:1; min-width:260px;">
|
||||||
@@ -306,7 +303,6 @@ function renderOrgHeader(org) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="text-secondary mt-8">Slug: ${esc(org.slug)} | Nutzer: ${org.user_count} | Lizenz: ${org.license_type || "Keine"} | Kontingent: <span id="orgKpiCredits">${esc(kpiText)}</span></div>
|
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-secondary btn-small" onclick="renameOrg()" title="Organisation umbenennen">
|
<button class="btn btn-secondary btn-small" onclick="renameOrg()" title="Organisation umbenennen">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg>
|
||||||
@@ -668,9 +664,13 @@ function setupForms() {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const errEl = document.getElementById("newOrgError");
|
const errEl = document.getElementById("newOrgError");
|
||||||
errEl.style.display = "none";
|
errEl.style.display = "none";
|
||||||
|
// Slug wird unsichtbar aus dem Namen erzeugt, bei Kollision haengt der
|
||||||
|
// Submit-Handler unten selbststaendig eine Nummer an.
|
||||||
|
const baseSlug = (document.getElementById("newOrgSlug").value ||
|
||||||
|
document.getElementById("newOrgName").value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")) || "organisation";
|
||||||
const body = {
|
const body = {
|
||||||
name: document.getElementById("newOrgName").value,
|
name: document.getElementById("newOrgName").value,
|
||||||
slug: document.getElementById("newOrgSlug").value,
|
slug: baseSlug,
|
||||||
output_language: document.getElementById("newOrgLanguage").value || "de",
|
output_language: document.getElementById("newOrgLanguage").value || "de",
|
||||||
};
|
};
|
||||||
// Optional direkt eine Lizenz mit Credits-Kontingent mitgeben
|
// Optional direkt eine Lizenz mit Credits-Kontingent mitgeben
|
||||||
@@ -697,8 +697,17 @@ function setupForms() {
|
|||||||
if (warn && warn.value) lic.budget_warning_percent = parseInt(warn.value);
|
if (warn && warn.value) lic.budget_warning_percent = parseInt(warn.value);
|
||||||
body.license = lic;
|
body.license = lic;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
let angelegt = false;
|
||||||
|
for (let i = 0; i < 8 && !angelegt; i++) {
|
||||||
|
body.slug = i === 0 ? baseSlug : baseSlug + "-" + (i + 1);
|
||||||
try {
|
try {
|
||||||
await API.post("/api/orgs", body);
|
await API.post("/api/orgs", body);
|
||||||
|
angelegt = true;
|
||||||
|
} catch (err) {
|
||||||
|
if (!(err.message || "").includes("Slug bereits vergeben") || i === 7) throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
closeModal("modalNewOrg");
|
closeModal("modalNewOrg");
|
||||||
document.getElementById("newOrgForm").reset();
|
document.getElementById("newOrgForm").reset();
|
||||||
onNewOrgLicenseToggle();
|
onNewOrgLicenseToggle();
|
||||||
@@ -1100,16 +1109,6 @@ async function loadOrgTokenUsage(orgId) {
|
|||||||
|
|
||||||
fillBudgetForm(budget);
|
fillBudgetForm(budget);
|
||||||
|
|
||||||
// Kontingent-Kurzinfo im Org-Kopf
|
|
||||||
const kpi = document.getElementById("orgKpiCredits");
|
|
||||||
if (kpi) {
|
|
||||||
if (unlimited) kpi.textContent = "unbegrenzt";
|
|
||||||
else if (budget.credits_available) {
|
|
||||||
const pct = budget.credits_percent_used != null ? ` (${budget.credits_percent_used.toFixed(0)}% verbraucht)` : "";
|
|
||||||
kpi.textContent = `${Math.round(budget.credits_used || 0).toLocaleString("de-DE")} von ${Math.round(budget.credits_available).toLocaleString("de-DE")} Credits${pct}`;
|
|
||||||
} else kpi.textContent = "kein Kontingent";
|
|
||||||
}
|
|
||||||
|
|
||||||
const tbody = document.getElementById('tokenMonthlyTable');
|
const tbody = document.getElementById('tokenMonthlyTable');
|
||||||
if (tbody) {
|
if (tbody) {
|
||||||
tbody.innerHTML = monthly.map(function(m) {
|
tbody.innerHTML = monthly.map(function(m) {
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren