Zugriff auf Mandanten eingerichtet
This commit is contained in:
@@ -256,7 +256,7 @@ if (in_array($page, ['overview', 'tenants'], true) && $pdo instanceof PDO) {
|
||||
<h2>Vorhandene Mandanten</h2>
|
||||
<div class="table">
|
||||
<table>
|
||||
<thead><tr><th>Name</th><th>Key</th><th>Status</th><th>Mitglieder</th><th>Admins</th><th>SSO</th><th>Aktion</th></tr></thead>
|
||||
<thead><tr><th>Name</th><th>Key</th><th>Status</th><th>Mitglieder</th><th>Admins</th><th>SSO</th><th>Aktionen</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($tenants as $tenant): ?>
|
||||
<tr>
|
||||
@@ -266,7 +266,17 @@ if (in_array($page, ['overview', 'tenants'], true) && $pdo instanceof PDO) {
|
||||
<td><?= admin_h((string) $tenant['member_count']) ?></td>
|
||||
<td><?= admin_h((string) $tenant['admin_count']) ?></td>
|
||||
<td><?= admin_h((string) $tenant['provider_count']) ?></td>
|
||||
<td><a class="button secondary" href="/admin/tenants/?edit=<?= admin_h((string) $tenant['id']) ?>">Bearbeiten</a></td>
|
||||
<td>
|
||||
<div class="actions">
|
||||
<a class="button secondary" href="/admin/tenants/?edit=<?= admin_h((string) $tenant['id']) ?>">Bearbeiten</a>
|
||||
<form method="post" action="/admin/tenants/">
|
||||
<input type="hidden" name="csrf" value="<?= admin_h($_SESSION['admin_csrf']) ?>">
|
||||
<input type="hidden" name="action" value="switch-tenant">
|
||||
<input type="hidden" name="tenant_id" value="<?= admin_h((string) $tenant['id']) ?>">
|
||||
<button type="submit">Mandant öffnen</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
|
||||
@@ -419,24 +419,394 @@ function app_handle_platform_tenant_action(PDO $pdo): void
|
||||
|
||||
$action = (string) ($_POST['action'] ?? '');
|
||||
|
||||
if ($action !== 'save-tenant') {
|
||||
app_require_platform_admin();
|
||||
|
||||
if ($action === 'save-tenant') {
|
||||
try {
|
||||
app_upsert_tenant($pdo, [
|
||||
'tenant_id' => (string) ($_POST['tenant_id'] ?? ''),
|
||||
'tenant_key' => (string) ($_POST['tenant_key'] ?? ''),
|
||||
'name' => (string) ($_POST['name'] ?? ''),
|
||||
'status' => (string) ($_POST['status'] ?? 'active'),
|
||||
]);
|
||||
} catch (Throwable $exception) {
|
||||
app_flash($exception->getMessage(), 'error');
|
||||
}
|
||||
|
||||
app_redirect('/admin/tenants/');
|
||||
}
|
||||
|
||||
if ($action === 'switch-tenant') {
|
||||
$tenantId = trim((string) ($_POST['tenant_id'] ?? ''));
|
||||
$admin = app_require_platform_admin();
|
||||
|
||||
try {
|
||||
app_enter_tenant_as_platform_admin($pdo, $admin, $tenantId);
|
||||
app_flash('Der Mandant wurde geöffnet. Du arbeitest jetzt mit erweitertem Global-Admin-Zugriff im Mandanten.', 'success');
|
||||
app_redirect('/dashboard/');
|
||||
} catch (Throwable $exception) {
|
||||
app_flash($exception->getMessage(), 'error');
|
||||
app_redirect('/admin/tenants/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function app_tenant_by_id(PDO $pdo, string $tenantId): ?array
|
||||
{
|
||||
return app_query_one(
|
||||
$pdo,
|
||||
'SELECT id, tenant_key, name, status FROM tenants WHERE id = :id LIMIT 1',
|
||||
['id' => $tenantId]
|
||||
);
|
||||
}
|
||||
|
||||
function app_enter_tenant_as_platform_admin(PDO $pdo, array $admin, string $tenantId): void
|
||||
{
|
||||
if ($tenantId === '') {
|
||||
throw new RuntimeException('Bitte wähle einen Mandanten aus.');
|
||||
}
|
||||
|
||||
$tenant = app_tenant_by_id($pdo, $tenantId);
|
||||
|
||||
if ($tenant === null) {
|
||||
throw new RuntimeException('Der ausgewählte Mandant konnte nicht gefunden werden.');
|
||||
}
|
||||
|
||||
app_set_auth_user([
|
||||
'user_id' => $admin['user_id'],
|
||||
'email' => $admin['email'],
|
||||
'display_name' => $admin['display_name'],
|
||||
'is_platform_admin' => true,
|
||||
'tenant_id' => $tenant['id'],
|
||||
'tenant_key' => $tenant['tenant_key'],
|
||||
'tenant_name' => $tenant['name'],
|
||||
'tenant_user_id' => null,
|
||||
'member_id' => null,
|
||||
'roles' => ['tenant_admin'],
|
||||
'acting_as_platform_admin' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
function app_user_by_email(PDO $pdo, string $email): ?array
|
||||
{
|
||||
return app_query_one(
|
||||
$pdo,
|
||||
'SELECT id, email, password_hash, display_name, is_platform_admin FROM users WHERE LOWER(email) = LOWER(:email) LIMIT 1',
|
||||
['email' => $email]
|
||||
);
|
||||
}
|
||||
|
||||
function app_tenant_user_by_id(PDO $pdo, string $tenantUserId, string $tenantId): ?array
|
||||
{
|
||||
return app_query_one(
|
||||
$pdo,
|
||||
<<<'SQL'
|
||||
SELECT
|
||||
tu.id,
|
||||
tu.tenant_id,
|
||||
tu.user_id,
|
||||
tu.status,
|
||||
u.email,
|
||||
u.display_name,
|
||||
u.password_hash,
|
||||
m.id AS member_id,
|
||||
m.display_name AS member_display_name,
|
||||
m.email AS member_email,
|
||||
m.status AS member_status,
|
||||
GROUP_CONCAT(DISTINCT r.role_key ORDER BY r.role_key SEPARATOR ',') AS role_keys
|
||||
FROM tenant_users tu
|
||||
INNER JOIN users u ON u.id = tu.user_id
|
||||
LEFT JOIN members m ON m.tenant_user_id = tu.id
|
||||
LEFT JOIN tenant_user_roles tur ON tur.tenant_user_id = tu.id
|
||||
LEFT JOIN roles r ON r.id = tur.role_id
|
||||
WHERE tu.id = :tenant_user_id
|
||||
AND tu.tenant_id = :tenant_id
|
||||
GROUP BY
|
||||
tu.id, tu.tenant_id, tu.user_id, tu.status,
|
||||
u.email, u.display_name, u.password_hash,
|
||||
m.id, m.display_name, m.email, m.status
|
||||
LIMIT 1
|
||||
SQL,
|
||||
['tenant_user_id' => $tenantUserId, 'tenant_id' => $tenantId]
|
||||
);
|
||||
}
|
||||
|
||||
function app_membership_row_for_user(PDO $pdo, string $tenantId, string $userId): ?array
|
||||
{
|
||||
return app_query_one(
|
||||
$pdo,
|
||||
<<<'SQL'
|
||||
SELECT
|
||||
tu.id,
|
||||
tu.tenant_id,
|
||||
tu.user_id,
|
||||
tu.status,
|
||||
u.email,
|
||||
u.display_name,
|
||||
u.password_hash,
|
||||
m.id AS member_id,
|
||||
m.display_name AS member_display_name,
|
||||
m.email AS member_email,
|
||||
m.status AS member_status,
|
||||
GROUP_CONCAT(DISTINCT r.role_key ORDER BY r.role_key SEPARATOR ',') AS role_keys
|
||||
FROM tenant_users tu
|
||||
INNER JOIN users u ON u.id = tu.user_id
|
||||
LEFT JOIN members m ON m.tenant_user_id = tu.id
|
||||
LEFT JOIN tenant_user_roles tur ON tur.tenant_user_id = tu.id
|
||||
LEFT JOIN roles r ON r.id = tur.role_id
|
||||
WHERE tu.tenant_id = :tenant_id
|
||||
AND tu.user_id = :user_id
|
||||
GROUP BY
|
||||
tu.id, tu.tenant_id, tu.user_id, tu.status,
|
||||
u.email, u.display_name, u.password_hash,
|
||||
m.id, m.display_name, m.email, m.status
|
||||
LIMIT 1
|
||||
SQL,
|
||||
['tenant_id' => $tenantId, 'user_id' => $userId]
|
||||
);
|
||||
}
|
||||
|
||||
function app_member_form_defaults(?array $member = null): array
|
||||
{
|
||||
$roleKeys = array_filter(array_map('trim', explode(',', (string) ($member['role_keys'] ?? ''))));
|
||||
|
||||
return [
|
||||
'tenant_user_id' => (string) ($member['id'] ?? ''),
|
||||
'display_name' => (string) ($member['member_display_name'] ?? $member['display_name'] ?? ''),
|
||||
'email' => (string) ($member['member_email'] ?? $member['email'] ?? ''),
|
||||
'status' => (string) ($member['member_status'] ?? $member['status'] ?? 'active'),
|
||||
'password' => '',
|
||||
'is_tenant_admin' => in_array('tenant_admin', $roleKeys, true),
|
||||
];
|
||||
}
|
||||
|
||||
function app_upsert_member(PDO $pdo, string $tenantId, array $data): void
|
||||
{
|
||||
$tenantUserId = trim((string) ($data['tenant_user_id'] ?? ''));
|
||||
$displayName = trim((string) ($data['display_name'] ?? ''));
|
||||
$email = strtolower(trim((string) ($data['email'] ?? '')));
|
||||
$status = trim((string) ($data['status'] ?? 'active'));
|
||||
$password = (string) ($data['password'] ?? '');
|
||||
$isTenantAdmin = !empty($data['is_tenant_admin']);
|
||||
|
||||
if ($displayName === '') {
|
||||
throw new RuntimeException('Bitte gib einen Namen für die Person an.');
|
||||
}
|
||||
|
||||
if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
throw new RuntimeException('Bitte gib eine gültige E-Mail-Adresse an.');
|
||||
}
|
||||
|
||||
if (!in_array($status, ['active', 'inactive'], true)) {
|
||||
$status = 'active';
|
||||
}
|
||||
|
||||
$tenant = app_tenant_by_id($pdo, $tenantId);
|
||||
|
||||
if ($tenant === null) {
|
||||
throw new RuntimeException('Der Mandant konnte nicht gefunden werden.');
|
||||
}
|
||||
|
||||
$existingUser = app_user_by_email($pdo, $email);
|
||||
$editingMembership = $tenantUserId !== '' ? app_tenant_user_by_id($pdo, $tenantUserId, $tenantId) : null;
|
||||
|
||||
if ($editingMembership === null && $tenantUserId !== '') {
|
||||
throw new RuntimeException('Die ausgewählte Person konnte im Mandanten nicht gefunden werden.');
|
||||
}
|
||||
|
||||
if ($existingUser === null && $password === '') {
|
||||
throw new RuntimeException('Für neue Personen ist ein Passwort erforderlich, damit die zentrale Anmeldung möglich ist.');
|
||||
}
|
||||
|
||||
if ($existingUser !== null && $editingMembership === null) {
|
||||
$otherTenantMembership = app_membership_row_for_user($pdo, $tenantId, (string) $existingUser['id']);
|
||||
|
||||
if ($otherTenantMembership !== null) {
|
||||
throw new RuntimeException('Diese E-Mail-Adresse ist im Mandanten bereits vorhanden.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($editingMembership !== null && $existingUser !== null && (string) $existingUser['id'] !== (string) $editingMembership['user_id']) {
|
||||
throw new RuntimeException('Diese E-Mail-Adresse gehört bereits zu einer anderen Person im Mandanten.');
|
||||
}
|
||||
|
||||
scripts_ensure_core_roles($pdo);
|
||||
$tenantAdminRoleId = scripts_role_id('tenant_admin', 'tenant');
|
||||
$now = date('Y-m-d H:i:s');
|
||||
|
||||
$pdo->beginTransaction();
|
||||
|
||||
try {
|
||||
if ($editingMembership !== null) {
|
||||
$userId = (string) $editingMembership['user_id'];
|
||||
|
||||
app_execute(
|
||||
$pdo,
|
||||
'UPDATE users SET email = :email, display_name = :display_name, updated_at = :updated_at' . ($password !== '' ? ', password_hash = :password_hash' : '') . ' WHERE id = :id',
|
||||
array_filter([
|
||||
'email' => $email,
|
||||
'display_name' => $displayName,
|
||||
'updated_at' => $now,
|
||||
'password_hash' => $password !== '' ? password_hash($password, PASSWORD_BCRYPT) : null,
|
||||
'id' => $userId,
|
||||
], static fn($value, $key): bool => $key !== 'password_hash' || $value !== null, ARRAY_FILTER_USE_BOTH)
|
||||
);
|
||||
|
||||
app_execute(
|
||||
$pdo,
|
||||
'UPDATE tenant_users SET status = :status, updated_at = :updated_at WHERE id = :id',
|
||||
[
|
||||
'status' => $status,
|
||||
'updated_at' => $now,
|
||||
'id' => $tenantUserId,
|
||||
]
|
||||
);
|
||||
|
||||
app_execute(
|
||||
$pdo,
|
||||
'UPDATE members SET display_name = :display_name, email = :email, status = :status, updated_at = :updated_at WHERE tenant_user_id = :tenant_user_id AND tenant_id = :tenant_id',
|
||||
[
|
||||
'display_name' => $displayName,
|
||||
'email' => $email,
|
||||
'status' => $status,
|
||||
'updated_at' => $now,
|
||||
'tenant_user_id' => $tenantUserId,
|
||||
'tenant_id' => $tenantId,
|
||||
]
|
||||
);
|
||||
} else {
|
||||
if ($existingUser !== null) {
|
||||
$userId = (string) $existingUser['id'];
|
||||
|
||||
app_execute(
|
||||
$pdo,
|
||||
'UPDATE users SET display_name = :display_name, updated_at = :updated_at' . ($password !== '' ? ', password_hash = :password_hash' : '') . ' WHERE id = :id',
|
||||
array_filter([
|
||||
'display_name' => $displayName,
|
||||
'updated_at' => $now,
|
||||
'password_hash' => $password !== '' ? password_hash($password, PASSWORD_BCRYPT) : null,
|
||||
'id' => $userId,
|
||||
], static fn($value, $key): bool => $key !== 'password_hash' || $value !== null, ARRAY_FILTER_USE_BOTH)
|
||||
);
|
||||
} else {
|
||||
$userId = app_uuid();
|
||||
|
||||
app_execute(
|
||||
$pdo,
|
||||
'INSERT INTO users (id, email, password_hash, display_name, is_platform_admin, created_at, updated_at) VALUES (:id, :email, :password_hash, :display_name, 0, :created_at, :updated_at)',
|
||||
[
|
||||
'id' => $userId,
|
||||
'email' => $email,
|
||||
'password_hash' => password_hash($password, PASSWORD_BCRYPT),
|
||||
'display_name' => $displayName,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$tenantUserId = app_uuid();
|
||||
|
||||
app_execute(
|
||||
$pdo,
|
||||
'INSERT INTO tenant_users (id, tenant_id, user_id, status, created_at, updated_at) VALUES (:id, :tenant_id, :user_id, :status, :created_at, :updated_at)',
|
||||
[
|
||||
'id' => $tenantUserId,
|
||||
'tenant_id' => $tenantId,
|
||||
'user_id' => $userId,
|
||||
'status' => $status,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]
|
||||
);
|
||||
|
||||
app_execute(
|
||||
$pdo,
|
||||
'INSERT INTO members (id, tenant_id, tenant_user_id, display_name, email, status, created_at, updated_at) VALUES (:id, :tenant_id, :tenant_user_id, :display_name, :email, :status, :created_at, :updated_at)',
|
||||
[
|
||||
'id' => app_uuid(),
|
||||
'tenant_id' => $tenantId,
|
||||
'tenant_user_id' => $tenantUserId,
|
||||
'display_name' => $displayName,
|
||||
'email' => $email,
|
||||
'status' => $status,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$existingRoleAssignment = app_query_one(
|
||||
$pdo,
|
||||
'SELECT id FROM tenant_user_roles WHERE tenant_user_id = :tenant_user_id AND role_id = :role_id LIMIT 1',
|
||||
['tenant_user_id' => $tenantUserId, 'role_id' => $tenantAdminRoleId]
|
||||
);
|
||||
|
||||
if ($isTenantAdmin && $existingRoleAssignment === null) {
|
||||
app_execute(
|
||||
$pdo,
|
||||
'INSERT INTO tenant_user_roles (id, tenant_user_id, role_id, created_at) VALUES (:id, :tenant_user_id, :role_id, :created_at)',
|
||||
[
|
||||
'id' => app_uuid(),
|
||||
'tenant_user_id' => $tenantUserId,
|
||||
'role_id' => $tenantAdminRoleId,
|
||||
'created_at' => $now,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if (!$isTenantAdmin && $existingRoleAssignment !== null) {
|
||||
app_execute(
|
||||
$pdo,
|
||||
'DELETE FROM tenant_user_roles WHERE id = :id',
|
||||
['id' => $existingRoleAssignment['id']]
|
||||
);
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
} catch (Throwable $exception) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
function app_handle_member_action(PDO $pdo, array $auth): void
|
||||
{
|
||||
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
|
||||
return;
|
||||
}
|
||||
|
||||
app_require_platform_admin();
|
||||
$action = (string) ($_POST['action'] ?? '');
|
||||
|
||||
if ($action !== 'save-member') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!app_can_manage_tenant($auth)) {
|
||||
app_flash('Für diese Aktion brauchst du Tenant-Admin-Rechte.', 'warning');
|
||||
app_redirect('/members/');
|
||||
}
|
||||
|
||||
$tenantId = (string) ($auth['tenant_id'] ?? '');
|
||||
|
||||
try {
|
||||
app_upsert_tenant($pdo, [
|
||||
'tenant_id' => (string) ($_POST['tenant_id'] ?? ''),
|
||||
'tenant_key' => (string) ($_POST['tenant_key'] ?? ''),
|
||||
'name' => (string) ($_POST['name'] ?? ''),
|
||||
app_upsert_member($pdo, $tenantId, [
|
||||
'tenant_user_id' => (string) ($_POST['tenant_user_id'] ?? ''),
|
||||
'display_name' => (string) ($_POST['display_name'] ?? ''),
|
||||
'email' => (string) ($_POST['email'] ?? ''),
|
||||
'status' => (string) ($_POST['status'] ?? 'active'),
|
||||
'password' => (string) ($_POST['password'] ?? ''),
|
||||
'is_tenant_admin' => isset($_POST['is_tenant_admin']),
|
||||
]);
|
||||
app_flash('Die Person wurde im Mandanten gespeichert.', 'success');
|
||||
} catch (Throwable $exception) {
|
||||
app_flash($exception->getMessage(), 'error');
|
||||
}
|
||||
|
||||
app_redirect('/admin/tenants/');
|
||||
app_redirect('/members/');
|
||||
}
|
||||
|
||||
function app_memberships_by_email(PDO $pdo, string $email): array
|
||||
@@ -827,18 +1197,21 @@ function app_members_for_tenant(PDO $pdo, string $tenantId): array
|
||||
$sql = <<<'SQL'
|
||||
SELECT
|
||||
m.id,
|
||||
tu.id AS tenant_user_id,
|
||||
tu.user_id,
|
||||
m.display_name,
|
||||
m.email,
|
||||
m.status,
|
||||
u.display_name AS user_display_name,
|
||||
GROUP_CONCAT(DISTINCT r.name ORDER BY r.name SEPARATOR ', ') AS roles
|
||||
GROUP_CONCAT(DISTINCT r.name ORDER BY r.name SEPARATOR ', ') AS roles,
|
||||
GROUP_CONCAT(DISTINCT r.role_key ORDER BY r.role_key SEPARATOR ',') AS role_keys
|
||||
FROM members m
|
||||
LEFT JOIN tenant_users tu ON tu.id = m.tenant_user_id
|
||||
LEFT JOIN users u ON u.id = tu.user_id
|
||||
LEFT JOIN tenant_user_roles tur ON tur.tenant_user_id = tu.id
|
||||
LEFT JOIN roles r ON r.id = tur.role_id
|
||||
WHERE m.tenant_id = :tenant_id
|
||||
GROUP BY m.id, m.display_name, m.email, m.status, u.display_name
|
||||
GROUP BY m.id, tu.id, tu.user_id, m.display_name, m.email, m.status, u.display_name
|
||||
ORDER BY m.display_name ASC
|
||||
SQL;
|
||||
|
||||
|
||||
@@ -80,6 +80,8 @@ $payments = [];
|
||||
$content = ['announcements' => [], 'faq' => []];
|
||||
$memberSummary = null;
|
||||
$loginFlow = ['state' => app_login_state(), 'message' => null, 'error' => null];
|
||||
$editingMember = null;
|
||||
$memberForm = app_member_form_defaults();
|
||||
|
||||
try {
|
||||
$pdo = app_pdo();
|
||||
@@ -108,6 +110,10 @@ if ($auth !== null && $pdo instanceof PDO) {
|
||||
app_handle_tenant_action($pdo, $auth);
|
||||
}
|
||||
|
||||
if ($page === 'members') {
|
||||
app_handle_member_action($pdo, $auth);
|
||||
}
|
||||
|
||||
try {
|
||||
$tenantDashboard = app_tenant_dashboard($pdo, (string) $auth['tenant_id']);
|
||||
$members = app_members_for_tenant($pdo, (string) $auth['tenant_id']);
|
||||
@@ -124,6 +130,11 @@ if ($auth !== null && $pdo instanceof PDO) {
|
||||
if ($page === 'content') {
|
||||
$content = app_content_for_tenant($pdo, (string) $auth['tenant_id']);
|
||||
}
|
||||
|
||||
if ($page === 'members' && isset($_GET['edit']) && $_GET['edit'] !== '') {
|
||||
$editingMember = app_tenant_user_by_id($pdo, (string) $_GET['edit'], (string) $auth['tenant_id']);
|
||||
$memberForm = app_member_form_defaults($editingMember);
|
||||
}
|
||||
} catch (Throwable $exception) {
|
||||
$dbError = $exception->getMessage();
|
||||
}
|
||||
@@ -164,6 +175,9 @@ $canManageTenant = app_can_manage_tenant($auth);
|
||||
<a href="/login/" class="<?= $page === 'login' ? 'active' : '' ?>">Anmeldung</a>
|
||||
<a href="<?= $auth !== null && app_is_platform_admin($auth) ? '/admin/' : '/admin/login/' ?>" class="<?= $page === 'tenants' ? 'active' : '' ?>"><?= $auth === null ? 'Für Betreiber' : 'Betreiber-Übersicht' ?></a>
|
||||
<?php if ($auth !== null): ?>
|
||||
<?php if (app_is_platform_admin($auth) && app_admin_user() !== null): ?>
|
||||
<a href="/admin/">Zentrale Verwaltung</a>
|
||||
<?php endif; ?>
|
||||
<a href="/dashboard/" class="<?= $page === 'dashboard' ? 'active' : '' ?>">Dashboard</a>
|
||||
<a href="/content/" class="<?= $page === 'content' ? 'active' : '' ?>">Hinweise</a>
|
||||
<?php if ($canManageTenant): ?>
|
||||
@@ -340,6 +354,12 @@ $canManageTenant = app_can_manage_tenant($auth);
|
||||
<div class="eyebrow">Tenant-Dashboard</div>
|
||||
<h1><?= h((string) $auth['tenant_name']) ?> auf einen Blick</h1>
|
||||
<p>Kontostand, Buchungen, Einzahlungen und die letzten Aktivitäten stehen direkt für diesen Tenant bereit.</p>
|
||||
<?php if (app_is_platform_admin($auth) && app_admin_user() !== null): ?>
|
||||
<div class="actions" style="margin-top:18px">
|
||||
<a class="button secondary" href="/admin/">Zur zentralen Verwaltung</a>
|
||||
<a class="button" href="/members/">Personen und Admins verwalten</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="grid grid-4">
|
||||
@@ -406,8 +426,67 @@ $canManageTenant = app_can_manage_tenant($auth);
|
||||
</div>
|
||||
</section>
|
||||
<?php elseif ($page === 'members'): ?>
|
||||
<section class="hero"><div class="eyebrow">Mitgliederverwaltung</div><h1>Mitglieder, Rollen und Aktivstatus</h1><p>Die zentrale Übersicht für Mitglieder, Rollen und den aktuellen Status innerhalb eines Tenants.</p></section>
|
||||
<section class="card"><div class="table"><table><thead><tr><th>Name</th><th>E-Mail</th><th>Status</th><th>Benutzer</th><th>Rollen</th></tr></thead><tbody><?php foreach ($members as $member): ?><tr><td><strong><?= h((string) $member['display_name']) ?></strong></td><td><?= h((string) $member['email']) ?></td><td><?= ((string) ($member['status'] ?? 'active')) === 'active' ? badge('Aktiv', 'success') : badge('Inaktiv', 'warning') ?></td><td><?= h((string) ($member['user_display_name'] ?? '')) ?></td><td><?= h((string) ($member['roles'] ?? 'Mitglied')) ?></td></tr><?php endforeach; ?></tbody></table></div></section>
|
||||
<section class="hero">
|
||||
<div class="eyebrow">Mitgliederverwaltung</div>
|
||||
<h1>Personen, Admins und Zugänge im Mandanten verwalten</h1>
|
||||
<p>Hier legst du neue Personen an, gibst Zugänge frei und weist bei Bedarf die Rolle als Mandanten-Admin zu.</p>
|
||||
<?php if (app_is_platform_admin($auth) && app_admin_user() !== null): ?>
|
||||
<div class="actions" style="margin-top:18px">
|
||||
<a class="button secondary" href="/admin/">Zur zentralen Verwaltung</a>
|
||||
<a class="button secondary" href="/dashboard/">Mandanten-Dashboard</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
<section class="grid grid-2">
|
||||
<article class="card">
|
||||
<h2><?= $editingMember !== null ? 'Person bearbeiten' : 'Neue Person anlegen' ?></h2>
|
||||
<form method="post" action="/members/" class="grid">
|
||||
<input type="hidden" name="action" value="save-member">
|
||||
<input type="hidden" name="tenant_user_id" value="<?= h((string) $memberForm['tenant_user_id']) ?>">
|
||||
<label>Name<input name="display_name" value="<?= h((string) $memberForm['display_name']) ?>" required></label>
|
||||
<label>E-Mail-Adresse<input type="email" name="email" value="<?= h((string) $memberForm['email']) ?>" required></label>
|
||||
<label>Status<select name="status"><option value="active"<?= $memberForm['status'] === 'active' ? ' selected' : '' ?>>Aktiv</option><option value="inactive"<?= $memberForm['status'] === 'inactive' ? ' selected' : '' ?>>Inaktiv</option></select></label>
|
||||
<label>Passwort<input type="password" name="password" placeholder="<?= $editingMember !== null ? 'Nur für Passwortwechsel ausfüllen' : 'Für die erste Anmeldung erforderlich' ?>"></label>
|
||||
<label class="checkbox" style="grid-column:1 / -1;display:flex;flex-direction:row;align-items:center;font-weight:600;">
|
||||
<input type="checkbox" name="is_tenant_admin"<?= !empty($memberForm['is_tenant_admin']) ? ' checked' : '' ?> style="width:auto">
|
||||
<span>Diese Person als Mandanten-Admin freischalten</span>
|
||||
</label>
|
||||
<div class="actions">
|
||||
<button type="submit">Speichern</button>
|
||||
<?php if ($editingMember !== null): ?>
|
||||
<a class="button secondary" href="/members/">Neue Person erfassen</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
<article class="card">
|
||||
<h2>Was der Global-Admin hier sehen kann</h2>
|
||||
<ul class="list">
|
||||
<li>Alle Personen des Mandanten inklusive Rollen und Aktivstatus.</li>
|
||||
<li>Ob ein Zugang nur Mitglied ist oder als Mandanten-Admin arbeiten darf.</li>
|
||||
<li>Den Mandanten aus Sicht der operativen Verwaltung, ohne den Global-Admin als Mitglied anlegen zu müssen.</li>
|
||||
</ul>
|
||||
</article>
|
||||
</section>
|
||||
<section class="card" style="margin-top:18px">
|
||||
<div class="table">
|
||||
<table>
|
||||
<thead><tr><th>Name</th><th>E-Mail</th><th>Status</th><th>Benutzer</th><th>Rollen</th><th>Aktion</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($members as $member): ?>
|
||||
<tr>
|
||||
<td><strong><?= h((string) $member['display_name']) ?></strong></td>
|
||||
<td><?= h((string) $member['email']) ?></td>
|
||||
<td><?= ((string) ($member['status'] ?? 'active')) === 'active' ? badge('Aktiv', 'success') : badge('Inaktiv', 'warning') ?></td>
|
||||
<td><?= h((string) ($member['user_display_name'] ?? '')) ?></td>
|
||||
<td><?= h((string) ($member['roles'] ?? 'Mitglied')) ?></td>
|
||||
<td><a class="button secondary" href="/members/?edit=<?= h((string) ($member['tenant_user_id'] ?? '')) ?>">Bearbeiten</a></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
<?php elseif ($page === 'ledger'): ?>
|
||||
<section class="hero"><div class="eyebrow">Buchungen</div><h1>Striche und Buchungen</h1><p>Kaffeeeinträge und alle zugehörigen Buchungen bleiben tenantweise nachvollziehbar.</p></section>
|
||||
<section class="grid grid-2">
|
||||
|
||||
Reference in New Issue
Block a user