User, Groups, Logs Update

This commit is contained in:
root committed 2025-10-29 15:38:25 +00:00
1 parent 4eea4b23bd
commit 36a6e2b66c
25 files changed
+3325 -1426

No files matched your search

Binary file not shown.
Binary file not shown.
Binary file not shown.
+129 -19
View File
@@ -2,16 +2,27 @@ import { db } from './index.js';
db.exec('PRAGMA foreign_keys = ON;');
const createRedeployLogsTable = `
CREATE TABLE IF NOT EXISTS redeploy_logs (
const createEventLogsTable = `
CREATE TABLE IF NOT EXISTS event_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
stack_id TEXT NOT NULL,
stack_name TEXT NOT NULL,
status TEXT NOT NULL,
category TEXT NOT NULL,
event_type TEXT,
action TEXT,
status TEXT,
severity TEXT,
entity_type TEXT,
entity_id TEXT,
entity_name TEXT,
actor_type TEXT,
actor_id TEXT,
actor_name TEXT,
source TEXT,
context_type TEXT,
context_id TEXT,
context_label TEXT,
message TEXT,
endpoint INTEGER,
redeploy_type TEXT
metadata TEXT
);
`;
@@ -43,6 +54,7 @@ CREATE TABLE IF NOT EXISTS user_groups (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
description TEXT,
avatar_color TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
@@ -172,20 +184,112 @@ CREATE TABLE IF NOT EXISTS user_settings (
);
`;
db.exec(createRedeployLogsTable);
const tableExists = (tableName) => {
return Boolean(
db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?")
.get(tableName)
);
};
try {
const columns = db.prepare('PRAGMA table_info(redeploy_logs)').all();
const hasRedeployType = columns.some((column) => column.name === 'redeploy_type');
if (!hasRedeployType) {
db.exec('ALTER TABLE redeploy_logs ADD COLUMN redeploy_type TEXT');
console.log('ℹ️ redeploy_type column hinzugefügt');
const serializeMetadata = (payload) => {
if (!payload || typeof payload !== 'object' || !Object.keys(payload).length) {
return null;
}
} catch (err) {
console.error('⚠️ Konnte redeploy_type Spalte nicht prüfen/erstellen:', err.message);
}
try {
return JSON.stringify(payload);
} catch (err) {
console.error('⚠️ Konnte Migrations-Metadaten nicht serialisieren:', err.message);
return null;
}
};
console.log('✅ redeploy_logs table ready');
db.exec(createEventLogsTable);
db.exec('CREATE INDEX IF NOT EXISTS idx_event_logs_timestamp ON event_logs (timestamp DESC);');
db.exec('CREATE INDEX IF NOT EXISTS idx_event_logs_category ON event_logs (category);');
db.exec('CREATE INDEX IF NOT EXISTS idx_event_logs_event_type ON event_logs (event_type);');
db.exec('CREATE INDEX IF NOT EXISTS idx_event_logs_entity ON event_logs (entity_type, entity_id);');
db.exec('CREATE INDEX IF NOT EXISTS idx_event_logs_context ON event_logs (context_type, context_id);');
console.log('✅ event_logs table ready');
if (tableExists('redeploy_logs')) {
try {
const legacyRows = db.prepare(`
SELECT id, timestamp, stack_id, stack_name, status, message, endpoint, redeploy_type
FROM redeploy_logs
ORDER BY id ASC
`).all();
if (legacyRows.length) {
const insertEventLog = db.prepare(`
INSERT INTO event_logs (
id,
timestamp,
category,
event_type,
action,
status,
entity_type,
entity_id,
entity_name,
context_type,
context_id,
message,
metadata
) VALUES (
@id,
@timestamp,
'stack',
@eventType,
'redeploy',
@status,
'stack',
@entityId,
@entityName,
@contextType,
@contextId,
@message,
@metadata
)
`);
const migrate = db.transaction((rows) => {
rows.forEach((row) => {
const contextType = row.endpoint !== null && row.endpoint !== undefined ? 'endpoint' : null;
const contextId = contextType ? String(row.endpoint) : null;
const metadataPayload = {
origin: 'redeploy_logs'
};
if (row.redeploy_type) {
metadataPayload.redeployType = row.redeploy_type;
}
if (contextId) {
metadataPayload.endpointId = contextId;
}
insertEventLog.run({
id: row.id,
timestamp: row.timestamp,
eventType: row.redeploy_type ?? null,
status: row.status ?? null,
entityId: row.stack_id !== undefined && row.stack_id !== null ? String(row.stack_id) : null,
entityName: row.stack_name ?? null,
contextType,
contextId,
message: row.message ?? null,
metadata: serializeMetadata(metadataPayload)
});
});
});
migrate(legacyRows);
console.log(`ℹ️ ${legacyRows.length} Einträge aus redeploy_logs migriert`);
}
db.exec('DROP TABLE redeploy_logs;');
console.log('ℹ️ Alte Tabelle redeploy_logs entfernt');
} catch (err) {
console.error('⚠️ Migration von redeploy_logs fehlgeschlagen:', err.message);
}
}
db.exec(createSettingsTable);
console.log('✅ settings table ready');
@@ -198,7 +302,13 @@ try {
const hasAvatarColor = userColumns.some((column) => column.name === 'avatar_color');
if (!hasAvatarColor) {
db.exec('ALTER TABLE users ADD COLUMN avatar_color TEXT');
console.log('ℹ️ avatar_color column hinzugefügt');
console.log('ℹ️ avatar_color column in Tabelle users hinzugefügt');
}
const userGroupColumns = db.prepare('PRAGMA table_info(user_groups)').all();
const hasAvatarColorGroup = userGroupColumns.some((column) => column.name === 'avatar_color');
if (!hasAvatarColorGroup) {
db.exec('ALTER TABLE user_groups ADD COLUMN avatar_color TEXT');
console.log('ℹ️ avatar_color column in Tabelle user_groups hinzugefügt');
}
const emailColumn = userColumns.find((column) => column.name === 'email');
if (emailColumn && emailColumn.notnull === 1) {
-181
View File
@@ -1,181 +0,0 @@
import { db } from './index.js';
const valueToArray = (value) => {
if (!value && value !== 0) return [];
const base = Array.isArray(value) ? value : [value];
return base
.flatMap((entry) => String(entry).split(','))
.map((entry) => entry.trim())
.filter(Boolean);
};
const singleValue = (value) => {
if (value === undefined || value === null) return undefined;
return Array.isArray(value) ? value[0] : value;
};
export function buildLogFilter(queryParams = {}) {
const filters = [];
const params = {};
const ids = valueToArray(queryParams.ids ?? queryParams.id)
.map((entry) => Number(entry))
.filter((value) => !Number.isNaN(value));
if (ids.length) {
const placeholders = ids.map((_, idx) => `@id${idx}`);
filters.push(`id IN (${placeholders.join(', ')})`);
ids.forEach((value, idx) => {
params[`id${idx}`] = value;
});
}
const stackIds = valueToArray(queryParams.stackIds ?? queryParams.stackId);
if (stackIds.length) {
const placeholders = stackIds.map((_, idx) => `@stackId${idx}`);
filters.push(`stack_id IN (${placeholders.join(', ')})`);
stackIds.forEach((stack, idx) => {
params[`stackId${idx}`] = stack;
});
}
const statuses = valueToArray(queryParams.statuses ?? queryParams.status);
if (statuses.length) {
const placeholders = statuses.map((_, idx) => `@status${idx}`);
filters.push(`status IN (${placeholders.join(', ')})`);
statuses.forEach((entry, idx) => {
params[`status${idx}`] = entry;
});
}
const endpoints = valueToArray(queryParams.endpoints ?? queryParams.endpoint);
if (endpoints.length) {
const placeholders = endpoints.map((_, idx) => `@endpoint${idx}`);
filters.push(`endpoint IN (${placeholders.join(', ')})`);
endpoints.forEach((entry, idx) => {
const numeric = Number(entry);
params[`endpoint${idx}`] = Number.isNaN(numeric) ? entry : numeric;
});
}
const redeployTypes = valueToArray(queryParams.redeployTypes ?? queryParams.redeployType);
if (redeployTypes.length) {
const placeholders = redeployTypes.map((_, idx) => `@redeployType${idx}`);
filters.push(`redeploy_type IN (${placeholders.join(', ')})`);
redeployTypes.forEach((entry, idx) => {
params[`redeployType${idx}`] = entry;
});
}
const messageQuery = singleValue(queryParams.message);
if (messageQuery && String(messageQuery).trim()) {
filters.push('message LIKE @message');
params.message = `%${String(messageQuery).trim()}%`;
}
const from = singleValue(queryParams.from);
if (from) {
filters.push('timestamp >= @from');
params.from = from;
}
const to = singleValue(queryParams.to);
if (to) {
filters.push('timestamp <= @to');
params.to = to;
}
return {
whereClause: filters.length ? `WHERE ${filters.join(' AND ')}` : '',
params,
};
}
const insertRedeployLogStmt = db.prepare(`
INSERT INTO redeploy_logs (stack_id, stack_name, status, message, endpoint, redeploy_type)
VALUES (@stackId, @stackName, @status, @message, @endpoint, @redeployType)
`);
export function logRedeployEvent({ stackId, stackName, status, message = null, endpoint = null, redeployType = null }) {
try {
insertRedeployLogStmt.run({
stackId: String(stackId),
stackName: stackName ?? 'Unknown',
status,
message,
endpoint,
redeployType: redeployType ?? null
});
} catch (err) {
console.error('❌ Fehler beim Speichern des Redeploy-Logs:', err.message);
}
}
export function deleteLogById(id) {
const stmt = db.prepare('DELETE FROM redeploy_logs WHERE id = ?');
const info = stmt.run(id);
return info.changes || 0;
}
export function deleteLogsByFilters(queryParams = {}) {
const { whereClause, params } = buildLogFilter(queryParams);
const stmt = db.prepare(`DELETE FROM redeploy_logs ${whereClause}`);
const info = stmt.run(params);
return info.changes || 0;
}
export function exportLogsByFilters(queryParams = {}, format = 'txt') {
const { whereClause, params } = buildLogFilter(queryParams);
const rows = db.prepare(`
SELECT id, timestamp, stack_id AS stackId, stack_name AS stackName, status, message, endpoint, redeploy_type AS redeployType
FROM redeploy_logs
${whereClause}
ORDER BY datetime(timestamp) DESC
`).all(params);
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
if (format === 'sql') {
const statements = rows.map((row) => {
const columns = ['id', 'timestamp', 'stack_id', 'stack_name', 'status', 'message', 'endpoint', 'redeploy_type'];
const values = [
row.id,
row.timestamp,
row.stackId,
row.stackName,
row.status,
row.message,
row.endpoint,
row.redeployType
].map((value) => {
if (value === null || value === undefined) return 'NULL';
return `'${String(value).replace(/'/g, "''")}'`;
});
return `INSERT INTO redeploy_logs (${columns.join(', ')}) VALUES (${values.join(', ')});`;
});
return {
filename: `redeploy-logs-${timestamp}.sql`,
contentType: 'application/sql; charset=utf-8',
content: statements.join('\n')
};
}
const lines = rows.map((row) => {
const parts = [
`[${row.id}]`,
row.timestamp,
`Stack: ${row.stackName ?? 'Unbekannt'} (ID: ${row.stackId})`,
`Status: ${row.status}`,
`Endpoint: ${row.endpoint ?? '-'}`,
`Nachricht: ${row.message ?? '-'}`,
`Redeploy: ${row.redeployType ?? '-'}`
];
return parts.join(' | ');
});
return {
filename: `redeploy-logs-${timestamp}.txt`,
contentType: 'text/plain; charset=utf-8',
content: lines.join('\n')
};
}
+246 -4
View File
@@ -1,10 +1,18 @@
import { db } from '../db/index.js';
import {
normalizeAvatarColor,
DEFAULT_AVATAR_COLOR,
SUPERUSER_GROUP_NAME,
pickRandomAvatarColor
} from '../auth/superuser.js';
import { logEvent } from '../logging/eventLogs.js';
const selectGroupsWithMembers = db.prepare(`
SELECT
g.id,
g.name,
g.description,
g.avatar_color,
g.created_at,
g.updated_at,
COUNT(DISTINCT u.id) AS member_count,
@@ -21,6 +29,7 @@ const selectGroupWithMembersById = db.prepare(`
g.id,
g.name,
g.description,
g.avatar_color,
g.created_at,
g.updated_at,
COUNT(DISTINCT u.id) AS member_count,
@@ -32,6 +41,12 @@ const selectGroupWithMembersById = db.prepare(`
GROUP BY g.id
`);
const selectGroupByIdBasic = db.prepare(`
SELECT id, name, description, avatar_color
FROM user_groups
WHERE id = ?
`);
const selectGroupIdByName = db.prepare(`
SELECT id
FROM user_groups
@@ -40,8 +55,28 @@ const selectGroupIdByName = db.prepare(`
`);
const insertGroupStatement = db.prepare(`
INSERT INTO user_groups (name, description, created_at, updated_at)
VALUES (?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
INSERT INTO user_groups (name, description, avatar_color, created_at, updated_at)
VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
`);
const updateGroupStatement = db.prepare(`
UPDATE user_groups
SET name = ?,
description = ?,
avatar_color = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`);
const countGroupMembersStatement = db.prepare(`
SELECT COUNT(*) AS member_count
FROM user_group_memberships
WHERE group_id = ?
`);
const deleteGroupStatement = db.prepare(`
DELETE FROM user_groups
WHERE id = ?
`);
const parseMembers = (rawPairs) => {
@@ -66,6 +101,7 @@ const sanitizeGroup = (row) => ({
id: row.id,
name: row.name,
description: row.description || '',
avatarColor: row.avatar_color || null,
createdAt: row.created_at || null,
updatedAt: row.updated_at || null,
memberCount: Number(row.member_count) || 0,
@@ -77,6 +113,15 @@ export function listGroups() {
return rows.map(sanitizeGroup);
}
export function getGroupById(groupId) {
const numericId = Number(groupId);
if (!Number.isFinite(numericId) || numericId <= 0) {
return null;
}
const row = selectGroupWithMembersById.get(numericId);
return row ? sanitizeGroup(row) : null;
}
export function createGroup({ name, description }) {
const normalizedName = typeof name === 'string' ? name.trim() : '';
if (!normalizedName) {
@@ -93,8 +138,205 @@ export function createGroup({ name, description }) {
}
const normalizedDescription = typeof description === 'string' ? description.trim() : null;
const insertResult = insertGroupStatement.run(normalizedName, normalizedDescription || null);
const avatarColor = pickRandomAvatarColor() || DEFAULT_AVATAR_COLOR;
const insertResult = insertGroupStatement.run(normalizedName, normalizedDescription || null, avatarColor);
const groupId = Number(insertResult.lastInsertRowid);
const row = selectGroupWithMembersById.get(groupId);
return row ? sanitizeGroup(row) : null;
const group = row ? sanitizeGroup(row) : null;
logEvent({
category: 'benutzergruppe',
eventType: 'gruppe-angelegt',
action: 'anlegen',
status: 'erfolgreich',
entityType: 'gruppe',
entityId: String(groupId),
entityName: group?.name ?? normalizedName,
message: `Benutzergruppe "${normalizedName}" angelegt`,
metadata: {
description: group?.description ?? normalizedDescription ?? null,
avatarColor
}
});
return group;
}
export function updateGroupDetails(groupId, { name, description, avatarColor } = {}) {
const numericId = Number(groupId);
if (!Number.isFinite(numericId) || numericId <= 0) {
const error = new Error('INVALID_GROUP_ID');
error.code = 'INVALID_GROUP_ID';
throw error;
}
const existingRow = selectGroupByIdBasic.get(numericId);
if (!existingRow) {
const error = new Error('GROUP_NOT_FOUND');
error.code = 'GROUP_NOT_FOUND';
throw error;
}
const normalizedName = typeof name === 'string' ? name.trim() : existingRow.name || '';
if (!normalizedName) {
const error = new Error('GROUP_NAME_REQUIRED');
error.code = 'GROUP_NAME_REQUIRED';
throw error;
}
const nameRow = selectGroupIdByName.get(normalizedName);
if (nameRow && Number(nameRow.id) !== numericId) {
const error = new Error('GROUP_NAME_TAKEN');
error.code = 'GROUP_NAME_TAKEN';
throw error;
}
let normalizedDescription;
if (description === undefined) {
normalizedDescription = existingRow.description ?? null;
} else if (description === null) {
normalizedDescription = null;
} else {
const trimmedDescription = typeof description === 'string' ? description.trim() : '';
normalizedDescription = trimmedDescription ? trimmedDescription : null;
}
const isSuperuserGroup = (existingRow.name || '').toLowerCase() === SUPERUSER_GROUP_NAME;
if (isSuperuserGroup) {
if (name !== undefined) {
const incomingName = typeof name === 'string' ? name.trim() : '';
if (incomingName && incomingName !== existingRow.name) {
const error = new Error('GROUP_SUPERUSER_PROTECTED');
error.code = 'GROUP_SUPERUSER_PROTECTED';
throw error;
}
}
if (description !== undefined) {
const existingDescriptionComparable = existingRow.description === null || existingRow.description === undefined
? ''
: String(existingRow.description).trim();
const incomingDescriptionComparable = description === null
? ''
: typeof description === 'string'
? description.trim()
: '';
if (incomingDescriptionComparable !== existingDescriptionComparable) {
const error = new Error('GROUP_SUPERUSER_PROTECTED');
error.code = 'GROUP_SUPERUSER_PROTECTED';
throw error;
}
}
// Enforce original values for protected fields
normalizedDescription = existingRow.description ?? null;
}
let colorToPersist = existingRow.avatar_color || null;
if (avatarColor !== undefined) {
const candidate = String(avatarColor || '').trim();
if (!candidate) {
colorToPersist = DEFAULT_AVATAR_COLOR;
} else {
const normalized = normalizeAvatarColor(candidate);
if (!normalized) {
const error = new Error('INVALID_AVATAR_COLOR');
error.code = 'INVALID_AVATAR_COLOR';
throw error;
}
colorToPersist = normalized;
}
}
updateGroupStatement.run(
isSuperuserGroup ? existingRow.name : normalizedName,
normalizedDescription,
colorToPersist,
numericId
);
const updatedGroup = getGroupById(numericId);
logEvent({
category: 'benutzergruppe',
eventType: 'gruppe-aktualisiert',
action: 'aktualisieren',
status: 'erfolgreich',
entityType: 'gruppe',
entityId: String(numericId),
entityName: updatedGroup?.name ?? normalizedName,
message: `Benutzergruppe "${updatedGroup?.name ?? normalizedName}" aktualisiert`,
metadata: {
previous: {
name: existingRow.name,
description: existingRow.description ?? null,
avatarColor: existingRow.avatar_color ?? null
},
current: updatedGroup
? {
name: updatedGroup.name,
description: updatedGroup.description ?? null,
avatarColor: updatedGroup.avatarColor ?? null,
memberCount: updatedGroup.memberCount ?? 0
}
: {
name: normalizedName,
description: normalizedDescription ?? null,
avatarColor: colorToPersist
}
}
});
return updatedGroup;
}
export function deleteGroup(groupId) {
const numericId = Number(groupId);
if (!Number.isFinite(numericId) || numericId <= 0) {
const error = new Error('INVALID_GROUP_ID');
error.code = 'INVALID_GROUP_ID';
throw error;
}
const existingRow = selectGroupByIdBasic.get(numericId);
if (!existingRow) {
const error = new Error('GROUP_NOT_FOUND');
error.code = 'GROUP_NOT_FOUND';
throw error;
}
const isSuperuserGroup = (existingRow.name || '').toLowerCase() === SUPERUSER_GROUP_NAME;
if (isSuperuserGroup) {
const error = new Error('GROUP_SUPERUSER_PROTECTED');
error.code = 'GROUP_SUPERUSER_PROTECTED';
throw error;
}
const { member_count: memberCount } = countGroupMembersStatement.get(numericId);
if (Number(memberCount) > 0) {
const error = new Error('GROUP_HAS_MEMBERS');
error.code = 'GROUP_HAS_MEMBERS';
error.memberCount = Number(memberCount);
throw error;
}
deleteGroupStatement.run(numericId);
logEvent({
category: 'benutzergruppe',
eventType: 'gruppe-gelöscht',
action: 'löschen',
status: 'erfolgreich',
entityType: 'gruppe',
entityId: String(numericId),
entityName: existingRow.name ?? `ID ${numericId}`,
message: `Benutzergruppe "${existingRow.name ?? numericId}" gelöscht`,
metadata: {
description: existingRow.description ?? null,
avatarColor: existingRow.avatar_color ?? null,
memberCountBeforeDelete: Number(memberCount)
}
});
}
+521 -129
View File
@@ -23,12 +23,12 @@ import {
verifyPassword
} from './auth/superuser.js';
import {
logRedeployEvent,
buildLogFilter,
deleteLogById,
deleteLogsByFilters,
exportLogsByFilters
} from './db/redeployLogs.js';
logEvent,
buildEventLogFilter,
deleteEventLogById,
deleteEventLogsByFilters,
exportEventLogsByFilters
} from './logging/eventLogs.js';
import { getSetting, setSetting, deleteSetting } from './db/settings.js';
import { activateMaintenanceMode, deactivateMaintenanceMode, getMaintenanceState, isMaintenanceModeActive } from './maintenance/state.js';
import {
@@ -45,8 +45,8 @@ import {
removeServer,
setServerApiKey
} from './setup/index.js';
import { listUsers, getUserById, updateUserGroups, createUser } from './users/index.js';
import { listGroups, createGroup } from './groups/index.js';
import { listUsers, getUserById, updateUserGroups, createUser, updateUserDetails, deleteUser, updateUserActiveStatus } from './users/index.js';
import { listGroups, createGroup, getGroupById, updateGroupDetails, deleteGroup } from './groups/index.js';
dotenv.config();
@@ -301,6 +301,53 @@ const REDEPLOY_TYPES = {
MAINTENANCE: 'Wartung'
};
const logStackEvent = ({
stackId,
stackName,
status,
message,
redeployType = null,
endpointId = null,
metadata = {}
}) => {
const metadataPayload = {
...metadata
};
if (redeployType) {
metadataPayload.redeployType = redeployType;
}
if (endpointId !== undefined && endpointId !== null) {
metadataPayload.endpointId = String(endpointId);
}
const hasMetadata = Object.keys(metadataPayload).length > 0;
logEvent({
category: 'stack',
eventType: redeployType ?? null,
action: 'redeploy',
status: status ?? null,
entityType: 'stack',
entityId: stackId !== undefined && stackId !== null ? String(stackId) : null,
entityName: stackName ?? null,
contextType: endpointId !== undefined && endpointId !== null ? 'endpoint' : null,
contextId: endpointId !== undefined && endpointId !== null ? String(endpointId) : null,
message: message ?? null,
metadata: hasMetadata ? metadataPayload : null
});
};
const parseJsonColumn = (value) => {
if (value === undefined || value === null || value === '') return null;
try {
return JSON.parse(value);
} catch {
return null;
}
};
const SELF_STACK_ID = process.env.SELF_STACK_ID ? String(process.env.SELF_STACK_ID) : null;
const PORTAINER_SCRIPT_SETTING_KEY = 'portainer_update_script';
const PORTAINER_SSH_CONFIG_KEY = 'portainer_ssh_config';
@@ -942,22 +989,40 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
});
maintenanceActivated = true;
logRedeployEvent({
stackId: 'portainer',
stackName: 'Portainer',
status: 'started',
logEvent({
category: 'wartung',
eventType: 'portainer-update',
action: 'aktualisieren',
status: 'gestartet',
entityType: 'service',
entityId: 'portainer',
entityName: 'Portainer',
contextType: 'endpoint',
contextId: String(endpointId),
message: `Portainer Update gestartet (Ziel: ${targetVersion ?? 'unbekannt'})`,
endpoint: endpointId,
redeployType: REDEPLOY_TYPES.MAINTENANCE
metadata: {
targetVersion: targetVersion ?? null,
scriptSource
},
source: 'system'
});
logRedeployEvent({
stackId: 'maintenance',
stackName: 'StackPulse Wartung',
status: 'started',
logEvent({
category: 'wartung',
eventType: 'Wartungsmodus',
action: 'aktivieren',
status: 'gestartet',
entityType: 'system',
entityId: 'wartung',
entityName: 'StackPulse Wartung',
contextType: 'endpoint',
contextId: String(endpointId),
message: 'Wartungsmodus aktiviert (Portainer Update)',
endpoint: endpointId,
redeployType: REDEPLOY_TYPES.MAINTENANCE
metadata: {
reason: 'portainer-update',
scriptSource
},
source: 'system'
});
updatePortainerState({
@@ -981,13 +1046,21 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
const statusAfter = await fetchPortainerStatusSummary().catch(() => null);
const finalVersion = statusAfter?.currentVersion ?? null;
logRedeployEvent({
stackId: 'portainer',
stackName: 'Portainer',
status: 'success',
logEvent({
category: 'wartung',
eventType: 'portainer-update',
action: 'aktualisieren',
status: 'erfolgreich',
entityType: 'service',
entityId: 'portainer',
entityName: 'Portainer',
contextType: 'endpoint',
contextId: String(endpointId),
message: `Portainer Update abgeschlossen (Version: ${finalVersion ?? 'unbekannt'})`,
endpoint: endpointId,
redeployType: REDEPLOY_TYPES.MAINTENANCE
metadata: {
resultVersion: finalVersion ?? null
},
source: 'system'
});
updatePortainerState({
@@ -1005,24 +1078,37 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
if (maintenanceActivated || maintenanceState?.active) {
deactivateMaintenanceMode({ message: 'Portainer Update abgeschlossen' });
maintenanceActivated = false;
logRedeployEvent({
stackId: 'maintenance',
stackName: 'StackPulse Wartung',
status: 'success',
logEvent({
category: 'wartung',
eventType: 'Wartungsmodus',
action: 'deaktivieren',
status: 'erfolgreich',
entityType: 'system',
entityId: 'wartung',
entityName: 'StackPulse Wartung',
contextType: 'endpoint',
contextId: String(endpointId),
message: 'Wartungsmodus deaktiviert',
endpoint: endpointId,
redeployType: REDEPLOY_TYPES.MAINTENANCE
metadata: {
reason: 'portainer-update'
},
source: 'system'
});
}
} catch (err) {
const message = err?.message || 'Portainer Update fehlgeschlagen';
logRedeployEvent({
stackId: 'portainer',
stackName: 'Portainer',
status: 'error',
logEvent({
category: 'wartung',
eventType: 'portainer-update',
action: 'aktualisieren',
status: 'fehler',
entityType: 'service',
entityId: 'portainer',
entityName: 'Portainer',
contextType: 'endpoint',
contextId: String(endpointId),
message,
endpoint: endpointId,
redeployType: REDEPLOY_TYPES.MAINTENANCE
source: 'system'
});
updatePortainerState({
@@ -1038,13 +1124,21 @@ const performPortainerUpdate = async ({ script, scriptSource, targetVersion }) =
if (maintenanceActivated || isMaintenanceModeActive()) {
deactivateMaintenanceMode({ message: 'Portainer Update fehlgeschlagen' });
logRedeployEvent({
stackId: 'maintenance',
stackName: 'StackPulse Wartung',
status: 'error',
logEvent({
category: 'wartung',
eventType: 'Wartungsmodus',
action: 'deaktivieren',
status: 'fehler',
entityType: 'system',
entityId: 'wartung',
entityName: 'StackPulse Wartung',
contextType: 'endpoint',
contextId: String(endpointId),
message: 'Wartungsmodus deaktiviert (Fehler)',
endpoint: endpointId,
redeployType: REDEPLOY_TYPES.MAINTENANCE
metadata: {
reason: 'portainer-update'
},
source: 'system'
});
}
} finally {
@@ -1191,12 +1285,12 @@ const redeployStackById = async (stackId, redeployType) => {
phase: REDEPLOY_PHASES.STARTED
});
logRedeployEvent({
logStackEvent({
stackId: targetId,
stackName: targetName,
status: 'started',
message: messages.started,
endpoint: stack.EndpointId,
endpointId: stack.EndpointId,
redeployType
});
@@ -1271,12 +1365,12 @@ const redeployStackById = async (stackId, redeployType) => {
phase: REDEPLOY_PHASES.SUCCESS
});
logRedeployEvent({
logStackEvent({
stackId: targetId,
stackName: targetName,
status: 'success',
message: messages.success,
endpoint: stack.EndpointId,
endpointId: stack.EndpointId,
redeployType
});
@@ -1293,12 +1387,12 @@ const redeployStackById = async (stackId, redeployType) => {
message: errorMessage
});
logRedeployEvent({
logStackEvent({
stackId: fallbackId,
stackName: fallbackName,
status: 'error',
message: errorMessage,
endpoint: stack?.EndpointId || endpointId,
endpointId: stack?.EndpointId || endpointId,
redeployType
});
@@ -1621,10 +1715,14 @@ app.post('/api/auth/login', (req, res) => {
}
const user = findUserByIdentifier(identifier);
if (!user || !user.is_active) {
if (!user) {
return res.status(401).json({ error: 'INVALID_CREDENTIALS' });
}
if (!user.is_active) {
return res.status(403).json({ error: 'USER_INACTIVE' });
}
const passwordValid = verifyPassword(password, user.password_hash, user.password_salt);
if (!passwordValid) {
return res.status(401).json({ error: 'INVALID_CREDENTIALS' });
@@ -1731,6 +1829,63 @@ app.get('/api/users/:userId', (req, res) => {
}
});
app.put('/api/users/:userId', (req, res) => {
const { userId } = req.params;
const numericId = Number(userId);
if (!Number.isFinite(numericId) || numericId <= 0) {
return res.status(400).json({ error: 'INVALID_USER_ID' });
}
const { username, email, password, avatarColor, groupId, groupIds } = req.body ?? {};
try {
const updatedUser = updateUserDetails(numericId, {
username,
email,
password,
avatarColor,
groupId,
groupIds
});
res.json({ item: updatedUser });
} catch (error) {
if (error.code === 'INVALID_USER_ID') {
return res.status(400).json({ error: 'INVALID_USER_ID' });
}
if (error.code === 'USER_NOT_FOUND') {
return res.status(404).json({ error: 'USER_NOT_FOUND' });
}
if (error.code === 'USERNAME_REQUIRED') {
return res.status(400).json({ error: 'USERNAME_REQUIRED' });
}
if (error.code === 'INVALID_EMAIL') {
return res.status(400).json({ error: 'INVALID_EMAIL' });
}
if (error.code === 'USERNAME_TAKEN') {
return res.status(409).json({ error: 'USERNAME_TAKEN' });
}
if (error.code === 'EMAIL_TAKEN') {
return res.status(409).json({ error: 'EMAIL_TAKEN' });
}
if (error.code === 'INVALID_PASSWORD' || error.code === 'PASSWORD_TOO_SHORT') {
return res.status(400).json({ error: error.code });
}
if (error.code === 'INVALID_AVATAR_COLOR') {
return res.status(400).json({ error: 'INVALID_AVATAR_COLOR' });
}
if (error.code === 'GROUP_NOT_FOUND') {
return res.status(400).json({ error: 'GROUP_NOT_FOUND', details: error.missingGroupIds || [] });
}
if (error.code === 'GROUP_SUPERUSER_PROTECTED') {
return res.status(403).json({ error: 'GROUP_SUPERUSER_PROTECTED' });
}
console.error(`⚠️ [Users] Aktualisierung der Benutzerdaten fehlgeschlagen (${userId}):`, error);
res.status(500).json({ error: 'USER_UPDATE_FAILED' });
}
});
app.put('/api/users/:userId/groups', (req, res) => {
const { userId } = req.params;
const numericId = Number(userId);
@@ -1758,6 +1913,61 @@ app.put('/api/users/:userId/groups', (req, res) => {
}
});
app.delete('/api/users/:userId', (req, res) => {
const { userId } = req.params;
const numericId = Number(userId);
if (!Number.isFinite(numericId) || numericId <= 0) {
return res.status(400).json({ error: 'INVALID_USER_ID' });
}
try {
deleteUser(numericId);
res.json({ success: true });
} catch (error) {
if (error.code === 'INVALID_USER_ID') {
return res.status(400).json({ error: 'INVALID_USER_ID' });
}
if (error.code === 'USER_NOT_FOUND') {
return res.status(404).json({ error: 'USER_NOT_FOUND' });
}
if (error.code === 'USER_SUPERUSER_PROTECTED') {
return res.status(403).json({ error: 'USER_SUPERUSER_PROTECTED' });
}
console.error(`⚠️ [Users] Löschen fehlgeschlagen (${userId}):`, error);
res.status(500).json({ error: 'USER_DELETE_FAILED' });
}
});
app.put('/api/users/:userId/active', (req, res) => {
const { userId } = req.params;
const numericId = Number(userId);
const { isActive } = req.body ?? {};
if (!Number.isFinite(numericId) || numericId <= 0) {
return res.status(400).json({ error: 'INVALID_USER_ID' });
}
try {
const updatedUser = updateUserActiveStatus(numericId, Boolean(isActive));
res.json({ item: updatedUser });
} catch (error) {
if (error.code === 'INVALID_USER_ID') {
return res.status(400).json({ error: 'INVALID_USER_ID' });
}
if (error.code === 'USER_NOT_FOUND') {
return res.status(404).json({ error: 'USER_NOT_FOUND' });
}
if (error.code === 'USER_SUPERUSER_PROTECTED') {
return res.status(403).json({ error: 'USER_SUPERUSER_PROTECTED' });
}
console.error(`⚠️ [Users] Aktualisierung des Aktivstatus fehlgeschlagen (${userId}):`, error);
res.status(500).json({ error: 'USER_STATUS_UPDATE_FAILED' });
}
});
app.get('/api/groups', (req, res) => {
try {
const groups = listGroups();
@@ -1768,6 +1978,26 @@ app.get('/api/groups', (req, res) => {
}
});
app.get('/api/groups/:groupId', (req, res) => {
const { groupId } = req.params;
const numericId = Number(groupId);
if (!Number.isFinite(numericId) || numericId <= 0) {
return res.status(400).json({ error: 'INVALID_GROUP_ID' });
}
try {
const group = getGroupById(numericId);
if (!group) {
return res.status(404).json({ error: 'GROUP_NOT_FOUND' });
}
res.json({ item: group });
} catch (error) {
console.error(`⚠️ [Groups] Abruf der Gruppendetails fehlgeschlagen (${groupId}):`, error);
res.status(500).json({ error: 'GROUP_FETCH_FAILED' });
}
});
app.post('/api/groups', (req, res) => {
const { name, description } = req.body ?? {};
try {
@@ -1785,6 +2015,74 @@ app.post('/api/groups', (req, res) => {
}
});
app.put('/api/groups/:groupId', (req, res) => {
const { groupId } = req.params;
const numericId = Number(groupId);
if (!Number.isFinite(numericId) || numericId <= 0) {
return res.status(400).json({ error: 'INVALID_GROUP_ID' });
}
const { name, description, avatarColor } = req.body ?? {};
try {
const updatedGroup = updateGroupDetails(numericId, { name, description, avatarColor });
res.json({ item: updatedGroup });
} catch (error) {
if (error.code === 'INVALID_GROUP_ID') {
return res.status(400).json({ error: 'INVALID_GROUP_ID' });
}
if (error.code === 'GROUP_NOT_FOUND') {
return res.status(404).json({ error: 'GROUP_NOT_FOUND' });
}
if (error.code === 'GROUP_NAME_REQUIRED') {
return res.status(400).json({ error: 'GROUP_NAME_REQUIRED' });
}
if (error.code === 'GROUP_NAME_TAKEN') {
return res.status(409).json({ error: 'GROUP_NAME_TAKEN' });
}
if (error.code === 'INVALID_AVATAR_COLOR') {
return res.status(400).json({ error: 'INVALID_AVATAR_COLOR' });
}
if (error.code === 'GROUP_SUPERUSER_PROTECTED') {
return res.status(403).json({ error: 'GROUP_SUPERUSER_PROTECTED' });
}
console.error(`⚠️ [Groups] Aktualisierung der Gruppendaten fehlgeschlagen (${groupId}):`, error);
res.status(500).json({ error: 'GROUP_UPDATE_FAILED' });
}
});
app.delete('/api/groups/:groupId', (req, res) => {
const { groupId } = req.params;
const numericId = Number(groupId);
if (!Number.isFinite(numericId) || numericId <= 0) {
return res.status(400).json({ error: 'INVALID_GROUP_ID' });
}
try {
deleteGroup(numericId);
res.json({ success: true });
} catch (error) {
if (error.code === 'INVALID_GROUP_ID') {
return res.status(400).json({ error: 'INVALID_GROUP_ID' });
}
if (error.code === 'GROUP_NOT_FOUND') {
return res.status(404).json({ error: 'GROUP_NOT_FOUND' });
}
if (error.code === 'GROUP_HAS_MEMBERS') {
return res.status(409).json({ error: 'GROUP_HAS_MEMBERS', memberCount: error.memberCount || 0 });
}
if (error.code === 'GROUP_SUPERUSER_PROTECTED') {
return res.status(403).json({ error: 'GROUP_SUPERUSER_PROTECTED' });
}
console.error(`⚠️ [Groups] Löschen der Gruppe fehlgeschlagen (${groupId}):`, error);
res.status(500).json({ error: 'GROUP_DELETE_FAILED' });
}
});
// Superuser Setup
app.get('/api/auth/superuser/status', (req, res) => {
const exists = hasSuperuser();
@@ -2198,13 +2496,16 @@ app.post('/api/maintenance/duplicates/cleanup', maintenanceGuard, async (req, re
return res.status(400).json({ error: 'Keine passenden Duplikate gefunden' });
}
logRedeployEvent({
logStackEvent({
stackId: target.canonical.Id,
stackName: target.canonical.Name,
status: 'started',
message: `Bereinigung doppelter Stacks gestartet (${duplicatesToDelete.length} Einträge)`,
endpoint: target.canonical.EndpointId,
redeployType: REDEPLOY_TYPES.MAINTENANCE
endpointId: target.canonical.EndpointId,
redeployType: REDEPLOY_TYPES.MAINTENANCE,
metadata: {
duplicateIds: duplicateIds
}
});
const results = [];
@@ -2238,13 +2539,16 @@ app.post('/api/maintenance/duplicates/cleanup', maintenanceGuard, async (req, re
if (errors.length) {
const failedIds = errors.map((entry) => entry.id).join(', ');
logRedeployEvent({
logStackEvent({
stackId: target.canonical.Id,
stackName: target.canonical.Name,
status: 'error',
message: `Bereinigung fehlgeschlagen für IDs: ${failedIds}`,
endpoint: target.canonical.EndpointId,
redeployType: REDEPLOY_TYPES.MAINTENANCE
endpointId: target.canonical.EndpointId,
redeployType: REDEPLOY_TYPES.MAINTENANCE,
metadata: {
duplicateIds: duplicateIds
}
});
return res.status(500).json({
@@ -2258,13 +2562,16 @@ app.post('/api/maintenance/duplicates/cleanup', maintenanceGuard, async (req, re
});
}
logRedeployEvent({
logStackEvent({
stackId: target.canonical.Id,
stackName: target.canonical.Name,
status: 'success',
message: `Bereinigung abgeschlossen. Entfernte IDs: ${results.map((entry) => entry.id).join(', ')}`,
endpoint: target.canonical.EndpointId,
redeployType: REDEPLOY_TYPES.MAINTENANCE
endpointId: target.canonical.EndpointId,
redeployType: REDEPLOY_TYPES.MAINTENANCE,
metadata: {
removedDuplicates: results.filter((entry) => entry.status === 'deleted').map((entry) => entry.id)
}
});
res.json({
@@ -2284,7 +2591,7 @@ app.post('/api/maintenance/duplicates/cleanup', maintenanceGuard, async (req, re
}
});
// Redeploy-Logs abrufen
// Event-Logs abrufen
app.get('/api/logs', (req, res) => {
const perPageParam = req.query.perPage ?? req.query.limit;
const perPage = perPageParam === 'all' ? 'all' : Math.min(parseInt(perPageParam, 10) || 50, 500);
@@ -2293,25 +2600,36 @@ app.get('/api/logs', (req, res) => {
const limit = perPage === 'all' ? undefined : perPage;
const offset = perPage === 'all' ? 0 : (page - 1) * perPage;
const { whereClause, params } = buildLogFilter(req.query);
const { whereClause, params } = buildEventLogFilter(req.query);
const baseQuery = `
SELECT
id,
timestamp,
stack_id AS stackId,
stack_name AS stackName,
category,
event_type AS eventType,
action,
status,
severity,
entity_type AS entityType,
entity_id AS entityId,
entity_name AS entityName,
actor_type AS actorType,
actor_id AS actorId,
actor_name AS actorName,
source,
context_type AS contextType,
context_id AS contextId,
context_label AS contextLabel,
message,
endpoint,
redeploy_type AS redeployType
FROM redeploy_logs
metadata
FROM event_logs
${whereClause}
ORDER BY datetime(timestamp) DESC
`;
const countQuery = `
SELECT COUNT(*) as total
FROM redeploy_logs
FROM event_logs
${whereClause}
`;
@@ -2326,7 +2644,25 @@ app.get('/api/logs', (req, res) => {
try {
const stmt = db.prepare(query);
const logs = stmt.all(params);
const rawLogs = stmt.all(params);
const logs = rawLogs.map((row) => {
const metadata = parseJsonColumn(row.metadata);
const legacyStack = row.entityType === 'stack' ? (row.entityId ?? null) : null;
const legacyStackName = row.entityType === 'stack' ? (row.entityName ?? row.entityId ?? null) : null;
const legacyEndpoint = row.contextType === 'endpoint' ? (row.contextId ?? null) : null;
return {
...row,
entityId: row.entityId ?? null,
actorId: row.actorId ?? null,
contextId: row.contextId ?? null,
metadata,
stackId: legacyStack,
stackName: legacyStackName,
redeployType: row.eventType ?? null,
endpoint: legacyEndpoint
};
});
const total = db.prepare(countQuery).get(params)?.total ?? logs.length;
res.json({
@@ -2336,11 +2672,8 @@ app.get('/api/logs', (req, res) => {
perPage: perPage === 'all' ? 'all' : limit
});
} catch (err) {
console.error('❌ Fehler beim Abrufen der Redeploy-Logs:', err.message);
if (err.message.includes('no such table')) {
return res.status(500).json({ error: 'redeploy_logs table nicht gefunden. Bitte Migration ausführen.' });
}
res.status(500).json({ error: 'Fehler beim Abrufen der Redeploy-Logs' });
console.error('❌ Fehler beim Abrufen der Event-Logs:', err.message);
res.status(500).json({ error: 'Fehler beim Abrufen der Logs' });
}
});
@@ -2351,24 +2684,24 @@ app.delete('/api/logs/:id', (req, res) => {
}
try {
const changes = deleteLogById(id);
const changes = deleteEventLogById(id);
if (!changes) {
return res.status(404).json({ error: 'Eintrag nicht gefunden' });
}
res.json({ success: true, deleted: changes });
} catch (err) {
console.error('❌ Fehler beim Löschen des Redeploy-Logs:', err.message);
res.status(500).json({ error: 'Fehler beim Löschen des Redeploy-Logs' });
console.error('❌ Fehler beim Löschen des Event-Logs:', err.message);
res.status(500).json({ error: 'Fehler beim Löschen des Logs' });
}
});
app.delete('/api/logs', (req, res) => {
try {
const deleted = deleteLogsByFilters(req.query);
const deleted = deleteEventLogsByFilters(req.query);
res.json({ success: true, deleted });
} catch (err) {
console.error('❌ Fehler beim Löschen der Redeploy-Logs:', err.message);
res.status(500).json({ error: 'Fehler beim Löschen der Redeploy-Logs' });
console.error('❌ Fehler beim Löschen der Event-Logs:', err.message);
res.status(500).json({ error: 'Fehler beim Löschen der Logs' });
}
});
@@ -2379,13 +2712,13 @@ app.get('/api/logs/export', (req, res) => {
}
try {
const payload = exportLogsByFilters(req.query, format);
const payload = exportEventLogsByFilters(req.query, format);
res.setHeader('Content-Type', payload.contentType);
res.setHeader('Content-Disposition', `attachment; filename="${payload.filename}"`);
res.send(payload.content);
} catch (err) {
console.error('❌ Fehler beim Export der Redeploy-Logs:', err.message);
res.status(500).json({ error: 'Fehler beim Export der Redeploy-Logs' });
console.error('❌ Fehler beim Export der Event-Logs:', err.message);
res.status(500).json({ error: 'Fehler beim Export der Logs' });
}
});
@@ -2409,8 +2742,10 @@ app.put('/api/stacks/:id/redeploy', async (req, res) => {
app.put('/api/stacks/redeploy-all', maintenanceGuard, async (req, res) => {
console.log(`🚀 PUT /api/stacks/redeploy-all: Redeploy ALL gestartet`);
let endpointId;
try {
const endpointId = requireActiveEndpointId();
endpointId = requireActiveEndpointId();
const stacksRes = await axiosInstance.get('/api/stacks');
const filteredStacks = stacksRes.data.filter(stack => String(stack.EndpointId) === String(endpointId));
@@ -2427,13 +2762,21 @@ app.put('/api/stacks/redeploy-all', maintenanceGuard, async (req, res) => {
const stackSummaryList = eligibleStacks.map((stack) => `${stack.Name} (${stack.Id})`);
const stackSummary = stackSummaryList.length ? stackSummaryList.join(', ') : 'keine Stacks';
logRedeployEvent({
stackId: '---',
stackName: '---',
status: 'started',
message: `Redeploy ALL gestartet für: ${stackSummary}`,
endpoint: endpointId,
redeployType: REDEPLOY_TYPES.ALL
logEvent({
category: 'stack',
eventType: REDEPLOY_TYPES.ALL,
action: 'redeploy-alle',
status: 'gestartet',
entityType: 'bulk-operation',
entityId: 'redeploy-alle',
entityName: 'Redeploy ALLE',
contextType: 'endpoint',
contextId: String(endpointId),
message: `Redeploy ALLE gestartet für: ${stackSummary}`,
metadata: {
stacks: stackSummaryList
},
source: 'system'
});
if (!eligibleStacks.length) {
@@ -2458,27 +2801,40 @@ app.put('/api/stacks/redeploy-all', maintenanceGuard, async (req, res) => {
}
}
logRedeployEvent({
stackId: '---',
stackName: '---',
status: 'success',
message: 'Redeploy ALL abgeschlossen',
endpoint: endpointId,
redeployType: REDEPLOY_TYPES.ALL
logEvent({
category: 'stack',
eventType: REDEPLOY_TYPES.ALL,
action: 'redeploy-alle',
status: 'erfolgreich',
entityType: 'bulk-operation',
entityId: 'redeploy-alle',
entityName: 'Redeploy ALLE',
contextType: 'endpoint',
contextId: String(endpointId),
message: 'Redeploy ALLE abgeschlossen',
metadata: {
processedStacks: stackSummaryList
},
source: 'system'
});
res.json({ success: true, message: 'Redeploy ALL abgeschlossen' });
} catch (err) {
const message = err.response?.data?.message || err.message;
logRedeployEvent({
stackId: '---',
stackName: '---',
logEvent({
category: 'stack',
eventType: REDEPLOY_TYPES.ALL,
action: 'redeploy-alle',
status: 'error',
entityType: 'bulk-operation',
entityId: 'redeploy-alle',
entityName: 'Redeploy ALLE',
contextType: endpointId !== undefined && endpointId !== null ? 'endpoint' : null,
contextId: endpointId !== undefined && endpointId !== null ? String(endpointId) : null,
message,
endpoint: endpointId,
redeployType: REDEPLOY_TYPES.ALL
source: 'system'
});
console.error('❌ Fehler bei Redeploy ALL:', message);
console.error('❌ Fehler bei Redeploy ALLE:', message);
res.status(500).json({ error: message });
}
});
@@ -2493,8 +2849,10 @@ app.put('/api/stacks/redeploy-selection', maintenanceGuard, async (req, res) =>
return res.status(400).json({ error: 'stackIds muss eine nicht leere Array sein' });
}
let endpointId;
try {
const endpointId = requireActiveEndpointId();
endpointId = requireActiveEndpointId();
const normalizedIds = stackIds.map((id) => String(id));
const { filteredStacks } = await loadStackCollections();
const stacksById = new Map(filteredStacks.map((stack) => [String(stack.Id), stack]));
@@ -2520,23 +2878,40 @@ app.put('/api/stacks/redeploy-selection', maintenanceGuard, async (req, res) =>
const summaryList = eligibleStacks.map((stack) => `${stack.Name} (${stack.Id})`);
const summaryText = summaryList.length ? summaryList.join(', ') : 'keine Stacks';
logRedeployEvent({
stackId: stackIds.join(','),
stackName: `Auswahl (${stackIds.length})`,
status: 'started',
logEvent({
category: 'stack',
eventType: REDEPLOY_TYPES.SELECTION,
action: 'redeploy-auswahl',
status: 'gestartet',
entityType: 'bulk-operation',
entityId: 'redeploy-auswahl',
entityName: `Redeploy Auswahl (${stackIds.length})`,
contextType: 'endpoint',
contextId: String(endpointId),
message: `Redeploy Auswahl gestartet für: ${summaryText}`,
endpoint: endpointId,
redeployType: REDEPLOY_TYPES.SELECTION
metadata: {
requestedStackIds: normalizedIds
},
source: 'system'
});
if (!eligibleStacks.length) {
logRedeployEvent({
stackId: stackIds.join(','),
stackName: `Auswahl (${stackIds.length})`,
status: 'success',
logEvent({
category: 'stack',
eventType: REDEPLOY_TYPES.SELECTION,
action: 'redeploy-auswahl',
status: 'erfolgreich',
entityType: 'bulk-operation',
entityId: 'redeploy-auswahl',
entityName: `Redeploy Auswahl (${stackIds.length})`,
contextType: 'endpoint',
contextId: String(endpointId),
message: 'Redeploy Auswahl übersprungen: keine veralteten Stacks',
endpoint: endpointId,
redeployType: REDEPLOY_TYPES.SELECTION
metadata: {
requestedStackIds: normalizedIds,
skipped: true
},
source: 'system'
});
return res.json({ success: true, message: 'Keine veralteten Stacks in der Auswahl' });
}
@@ -2558,25 +2933,42 @@ app.put('/api/stacks/redeploy-selection', maintenanceGuard, async (req, res) =>
}
}
logRedeployEvent({
stackId: stackIds.join(','),
stackName: `Auswahl (${stackIds.length})`,
status: 'success',
logEvent({
category: 'stack',
eventType: REDEPLOY_TYPES.SELECTION,
action: 'redeploy-auswahl',
status: 'erfolgreich',
entityType: 'bulk-operation',
entityId: 'redeploy-auswahl',
entityName: `Redeploy Auswahl (${stackIds.length})`,
contextType: 'endpoint',
contextId: String(endpointId),
message: 'Redeploy Auswahl abgeschlossen',
endpoint: endpointId,
redeployType: REDEPLOY_TYPES.SELECTION
metadata: {
processedStackIds: eligibleStacks.map((stack) => String(stack.Id))
},
source: 'system'
});
res.json({ success: true, message: 'Redeploy Auswahl abgeschlossen' });
} catch (err) {
const message = err.response?.data?.message || err.message;
logRedeployEvent({
stackId: Array.isArray(stackIds) ? stackIds.join(',') : String(stackIds ?? ''),
stackName: `Auswahl (${Array.isArray(stackIds) ? stackIds.length : 0})`,
status: 'error',
const normalized = Array.isArray(stackIds) ? stackIds.map((id) => String(id)) : [];
logEvent({
category: 'stack',
eventType: REDEPLOY_TYPES.SELECTION,
action: 'redeploy-auswahl',
status: 'fehler',
entityType: 'bulk-operation',
entityId: 'redeploy-auswahl',
entityName: `Redeploy Auswahl (${Array.isArray(stackIds) ? stackIds.length : 0})`,
contextType: endpointId !== undefined && endpointId !== null ? 'endpoint' : null,
contextId: endpointId !== undefined && endpointId !== null ? String(endpointId) : null,
message,
endpoint: endpointId,
redeployType: REDEPLOY_TYPES.SELECTION
metadata: {
requestedStackIds: normalized
},
source: 'system'
});
console.error('❌ Fehler bei Redeploy Auswahl:', message);
res.status(500).json({ error: message });
+331
View File
@@ -0,0 +1,331 @@
import { db } from '../db/index.js';
const valueToArray = (value) => {
if (value === undefined || value === null) return [];
const base = Array.isArray(value) ? value : [value];
return base
.flatMap((entry) => String(entry).split(','))
.map((entry) => entry.trim())
.filter((entry) => entry.length > 0);
};
const singleValue = (value) => {
if (value === undefined || value === null) return undefined;
return Array.isArray(value) ? value[0] : value;
};
const serializeJson = (value) => {
if (value === undefined || value === null || value === '') return null;
if (typeof value === 'string') return value;
try {
return JSON.stringify(value);
} catch (err) {
console.error('⚠️ Konnte JSON nicht serialisieren:', err.message);
return JSON.stringify({ serializationError: true });
}
};
const appendInFilter = (filters, params, values, column, prefix) => {
if (!values.length) return;
const placeholders = values.map((_, idx) => {
const key = `${prefix}${idx}`;
params[key] = values[idx];
return `@${key}`;
});
filters.push(`${column} IN (${placeholders.join(', ')})`);
};
export function buildEventLogFilter(queryParams = {}) {
const filters = [];
const params = {};
const ids = valueToArray(queryParams.ids ?? queryParams.id)
.map((entry) => Number(entry))
.filter((value) => Number.isFinite(value));
if (ids.length) {
appendInFilter(filters, params, ids, 'id', 'id');
}
const categories = valueToArray(queryParams.categories ?? queryParams.category);
appendInFilter(filters, params, categories, 'category', 'category');
const statuses = valueToArray(queryParams.statuses ?? queryParams.status);
appendInFilter(filters, params, statuses, 'status', 'status');
const actions = valueToArray(queryParams.actions ?? queryParams.action);
appendInFilter(filters, params, actions, 'action', 'action');
const eventTypes = [
...valueToArray(queryParams.eventTypes ?? queryParams.eventType),
...valueToArray(queryParams.redeployTypes ?? queryParams.redeployType)
];
if (eventTypes.length) {
const unique = Array.from(new Set(eventTypes));
appendInFilter(filters, params, unique, 'event_type', 'eventType');
}
const severities = valueToArray(queryParams.severities ?? queryParams.severity);
appendInFilter(filters, params, severities, 'severity', 'severity');
const entityTypes = valueToArray(queryParams.entityTypes ?? queryParams.entityType);
appendInFilter(filters, params, entityTypes, 'entity_type', 'entityType');
const entityIds = valueToArray(queryParams.entityIds ?? queryParams.entityId);
appendInFilter(filters, params, entityIds, 'entity_id', 'entityId');
const actorTypes = valueToArray(queryParams.actorTypes ?? queryParams.actorType);
appendInFilter(filters, params, actorTypes, 'actor_type', 'actorType');
const actorIds = valueToArray(queryParams.actorIds ?? queryParams.actorId);
appendInFilter(filters, params, actorIds, 'actor_id', 'actorId');
const sources = valueToArray(queryParams.sources ?? queryParams.source);
appendInFilter(filters, params, sources, 'source', 'source');
const contextTypes = valueToArray(queryParams.contextTypes ?? queryParams.contextType);
appendInFilter(filters, params, contextTypes, 'context_type', 'contextType');
const contextIds = valueToArray(queryParams.contextIds ?? queryParams.contextId);
appendInFilter(filters, params, contextIds, 'context_id', 'contextId');
const legacyStackIds = valueToArray(queryParams.stackIds ?? queryParams.stackId);
if (legacyStackIds.length) {
const placeholders = legacyStackIds.map((_, idx) => {
const key = `legacyStackId${idx}`;
params[key] = legacyStackIds[idx];
return `@${key}`;
});
filters.push(`(entity_type = 'stack' AND entity_id IN (${placeholders.join(', ')}))`);
}
const legacyEndpoints = valueToArray(queryParams.endpoints ?? queryParams.endpoint);
if (legacyEndpoints.length) {
const placeholders = legacyEndpoints.map((_, idx) => {
const key = `legacyEndpoint${idx}`;
params[key] = legacyEndpoints[idx];
return `@${key}`;
});
filters.push(`(context_type = 'endpoint' AND context_id IN (${placeholders.join(', ')}))`);
}
const messageQuery = singleValue(queryParams.message ?? queryParams.text);
if (messageQuery && String(messageQuery).trim()) {
filters.push('message LIKE @message');
params.message = `%${String(messageQuery).trim()}%`;
}
const search = singleValue(queryParams.search ?? queryParams.q);
if (search && String(search).trim()) {
filters.push('(message LIKE @search OR entity_name LIKE @search OR metadata LIKE @search)');
params.search = `%${String(search).trim()}%`;
}
const from = singleValue(queryParams.from ?? queryParams.start);
if (from) {
filters.push('timestamp >= @from');
params.from = from;
}
const to = singleValue(queryParams.to ?? queryParams.end);
if (to) {
filters.push('timestamp <= @to');
params.to = to;
}
return {
whereClause: filters.length ? `WHERE ${filters.join(' AND ')}` : '',
params
};
}
const insertEventLogStmt = db.prepare(`
INSERT INTO event_logs (
timestamp,
category,
event_type,
action,
status,
severity,
entity_type,
entity_id,
entity_name,
actor_type,
actor_id,
actor_name,
source,
context_type,
context_id,
context_label,
message,
metadata
)
VALUES (
COALESCE(@timestamp, CURRENT_TIMESTAMP),
@category,
@eventType,
@action,
@status,
@severity,
@entityType,
@entityId,
@entityName,
@actorType,
@actorId,
@actorName,
@source,
@contextType,
@contextId,
@contextLabel,
@message,
@metadata
)
`);
export function logEvent(event = {}) {
if (!event.category) {
console.error('⚠️ logEvent wurde ohne Kategorie aufgerufen');
return;
}
try {
insertEventLogStmt.run({
timestamp: event.timestamp ?? null,
category: event.category,
eventType: event.eventType ?? null,
action: event.action ?? null,
status: event.status ?? null,
severity: event.severity ?? null,
entityType: event.entityType ?? null,
entityId: event.entityId !== undefined && event.entityId !== null ? String(event.entityId) : null,
entityName: event.entityName ?? null,
actorType: event.actorType ?? null,
actorId: event.actorId !== undefined && event.actorId !== null ? String(event.actorId) : null,
actorName: event.actorName ?? null,
source: event.source ?? null,
contextType: event.contextType ?? null,
contextId: event.contextId !== undefined && event.contextId !== null ? String(event.contextId) : null,
contextLabel: event.contextLabel ?? null,
message: event.message ?? null,
metadata: serializeJson(event.metadata)
});
} catch (err) {
console.error('❌ Fehler beim Speichern des Event-Logs:', err.message);
}
}
export function deleteEventLogById(id) {
const stmt = db.prepare('DELETE FROM event_logs WHERE id = ?');
const info = stmt.run(id);
return info.changes || 0;
}
export function deleteEventLogsByFilters(queryParams = {}) {
const { whereClause, params } = buildEventLogFilter(queryParams);
const stmt = db.prepare(`DELETE FROM event_logs ${whereClause}`);
const info = stmt.run(params);
return info.changes || 0;
}
export function exportEventLogsByFilters(queryParams = {}, format = 'txt') {
const { whereClause, params } = buildEventLogFilter(queryParams);
const rows = db.prepare(`
SELECT
id,
timestamp,
category,
event_type AS eventType,
action,
status,
severity,
entity_type AS entityType,
entity_id AS entityId,
entity_name AS entityName,
actor_type AS actorType,
actor_id AS actorId,
actor_name AS actorName,
source,
context_type AS contextType,
context_id AS contextId,
context_label AS contextLabel,
message,
metadata
FROM event_logs
${whereClause}
ORDER BY datetime(timestamp) DESC
`).all(params);
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
if (format === 'sql') {
const columns = [
'id',
'timestamp',
'category',
'event_type',
'action',
'status',
'severity',
'entity_type',
'entity_id',
'entity_name',
'actor_type',
'actor_id',
'actor_name',
'source',
'context_type',
'context_id',
'context_label',
'message',
'metadata'
];
const statements = rows.map((row) => {
const values = columns.map((column) => {
const key = {
event_type: 'eventType',
entity_type: 'entityType',
entity_id: 'entityId',
entity_name: 'entityName',
actor_type: 'actorType',
actor_id: 'actorId',
actor_name: 'actorName',
context_type: 'contextType',
context_id: 'contextId',
context_label: 'contextLabel'
}[column] ?? column;
const value = row[key];
if (value === null || value === undefined) return 'NULL';
return `'${String(value).replace(/'/g, "''")}'`;
});
return `INSERT INTO event_logs (${columns.join(', ')}) VALUES (${values.join(', ')});`;
});
return {
filename: `event-logs-${timestamp}.sql`,
contentType: 'application/sql; charset=utf-8',
content: statements.join('\n')
};
}
const lines = rows.map((row) => {
const parts = [
`[${row.id}]`,
row.timestamp,
`Kategorie: ${row.category}`,
row.eventType ? `Typ: ${row.eventType}` : null,
row.action ? `Aktion: ${row.action}` : null,
row.status ? `Status: ${row.status}` : null,
row.entityName ? `Entität: ${row.entityName}${row.entityId ? ` (#${row.entityId})` : ''}` : row.entityId ? `Entität-ID: ${row.entityId}` : null,
row.contextType ? `Kontext: ${row.contextType}${row.contextId ? ` (#${row.contextId}${row.contextLabel ? ` – ${row.contextLabel}` : ''})` : ''}` : null,
row.message ? `Nachricht: ${row.message}` : null,
row.metadata ? `Metadaten: ${row.metadata}` : null
].filter(Boolean);
return parts.join(' | ');
});
return {
filename: `event-logs-${timestamp}.txt`,
contentType: 'text/plain; charset=utf-8',
content: lines.join('\n')
};
}
+27
View File
@@ -1,4 +1,5 @@
import { getJsonSetting, setJsonSetting } from '../db/settings.js';
import { logEvent } from '../logging/eventLogs.js';
const MAINTENANCE_KEY = 'maintenance_mode';
@@ -44,6 +45,19 @@ export function isMaintenanceModeActive() {
}
export function activateMaintenanceMode({ message = null, extra = null } = {}) {
logEvent({
category: 'wartung',
eventType: 'Wartungsmodus',
action: 'aktivieren',
status: 'gestartet',
entityType: 'system',
entityId: 'wartung',
entityName: 'StackPulse Wartung',
contextType: 'System',
contextId: 'System',
message: 'Wartungsmodus aktiviert',
source: 'system'
});
const now = new Date().toISOString();
return persistState({
active: true,
@@ -54,6 +68,19 @@ export function activateMaintenanceMode({ message = null, extra = null } = {}) {
}
export function deactivateMaintenanceMode({ message = null } = {}) {
logEvent({
category: 'wartung',
eventType: 'wartungsmodus',
action: 'deaktivieren',
status: 'erfolgreich',
entityType: 'system',
entityId: 'wartung',
entityName: 'StackPulse Wartung',
contextType: 'System',
contextId: 'System',
message: 'Wartungsmodus deaktiviert',
source: 'system'
});
return persistState({
active: false,
message,
+1
View File
@@ -0,0 +1 @@
*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#eee}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Roboto,sans-serif;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#bdbdbd}input::placeholder,textarea::placeholder{opacity:1;color:#bdbdbd}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(33 150 243 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(33 150 243 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width: 540px){.container{max-width:540px}}@media (min-width: 720px){.container{max-width:720px}}@media (min-width: 960px){.container{max-width:960px}}@media (min-width: 1140px){.container{max-width:1140px}}@media (min-width: 1320px){.container{max-width:1320px}}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.\!invisible{visibility:hidden!important}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.\!absolute{position:absolute!important}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bLine truncated
-1
View File
@@ -1 +0,0 @@
*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#eee}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Roboto,sans-serif;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#bdbdbd}input::placeholder,textarea::placeholder{opacity:1;color:#bdbdbd}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(33 150 243 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(33 150 243 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width: 540px){.container{max-width:540px}}@media (min-width: 720px){.container{max-width:720px}}@media (min-width: 960px){.container{max-width:960px}}@media (min-width: 1140px){.container{max-width:1140px}}@media (min-width: 1320px){.container{max-width:1320px}}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.\!invisible{visibility:hidden!important}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.\!absolute{position:absolute!important}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bLine truncated
-93
View File
@@ -1,93 +0,0 @@
function jj(e,t){for(var r=0;r<t.length;r++){const n=t[r];if(typeof n!="string"&&!Array.isArray(n)){for(const o in n)if(o!=="default"&&!(o in e)){const a=Object.getOwnPropertyDescriptor(n,o);a&&Object.defineProperty(e,o,a.get?a:{enumerable:!0,get:()=>n[o]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))n(o);new MutationObserver(o=>{for(const a of o)if(a.type==="childList")for(const i of a.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&n(i)}).observe(document,{childList:!0,subtree:!0});function r(o){const a={};return o.integrity&&(a.integrity=o.integrity),o.referrerPolicy&&(a.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?a.credentials="include":o.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function n(o){if(o.ep)return;o.ep=!0;const a=r(o);fetch(o.href,a)}})();function Mx(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function Ks(e){if(e.__esModule)return e;var t=e.default;if(typeof t=="function"){var r=function n(){return this instanceof n?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};r.prototype=t.prototype}else r={};return Object.defineProperty(r,"__esModule",{value:!0}),Object.keys(e).forEach(function(n){var o=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(r,n,o.get?o:{enumerable:!0,get:function(){return e[n]}})}),r}var Ix={exports:{}},Rd={},Dx={exports:{}},pt={};/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var Ys=Symbol.for("react.element"),Rj=Symbol.for("react.portal"),Aj=Symbol.for("react.fragment"),Mj=Symbol.for("react.strict_mode"),Ij=Symbol.for("react.profiler"),Dj=Symbol.for("react.provider"),Lj=Symbol.for("react.context"),Fj=Symbol.for("react.forward_ref"),zj=Symbol.for("react.suspense"),Bj=Symbol.for("react.memo"),Vj=Symbol.for("react.lazy"),Hv=Symbol.iterator;function Wj(e){return e===null||typeof e!="object"?null:(e=Hv&&e[Hv]||e["@@iterator"],typeof e=="function"?e:null)}var Lx={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Fx=Object.assign,zx={};function al(e,t,r){this.props=e,this.context=t,this.refs=zx,this.updater=r||Lx}al.prototype.isReactComponent={};al.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};al.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Bx(){}Bx.prototype=al.prototype;function qh(e,t,r){this.props=e,this.context=t,this.refs=zx,this.updater=r||Lx}var Gh=qh.prototype=new Bx;Gh.constructor=qh;Fx(Gh,al.prototype);Gh.isPureReactComponent=!0;var $v=Array.isArray,Vx=Object.prototype.hasOwnProperty,Kh={current:null},Wx={key:!0,ref:!0,__self:!0,__source:!0};function Ux(e,t,r){var n,o={},a=null,i=null;if(t!=null)for(n in t.ref!==void 0&&(i=t.ref),t.key!==void 0&&(a=""+t.key),t)Vx.call(t,n)&&!Wx.hasOwnProperty(n)&&(o[n]=t[n]);var u=arguments.length-2;if(u===1)o.children=r;else if(1<u){for(var f=Array(u),g=0;g<u;g++)f[g]=arguments[g+2];o.children=f}if(e&&e.defaultProps)for(n in u=e.defaultProps,u)o[n]===void 0&&(o[n]=u[n]);return{$$typeof:Ys,type:e,key:a,ref:i,props:o,_owner:Kh.current}}function Uj(e,t){return{$$typeof:Ys,type:e.type,key:t,ref:e.ref,props:e.props,_owner:e._owner}}function Yh(e){return typeof e=="object"&&e!==null&&e.$$typeof===Ys}function Hj(e){var t={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,function(r){return t[r]})}var qv=/\/+/g;function pp(e,t){return typeof e=="object"&&e!==null&&e.key!=null?Hj(""+e.key):t.toString(36)}function ac(e,t,r,n,o){var a=typeof e;(a==="undefined"||a==="boolean")&&(e=null);var i=!1;if(e===null)i=!0;else switch(a){case"string":case"number":i=!0;break;case"object":switch(e.$$typeof){case Ys:case Rj:i=!0}}if(i)return i=e,o=o(i),e=n===""?"."+pp(i,0):n,$v(o)?(r="",e!=null&&(r=e.replace(qv,"$&/")+"/"),ac(o,t,r,"",function(g){return g})):o!=null&&(Yh(o)&&(o=Uj(o,r+(!o.key||i&&i.key===o.key?"":(""+o.key).replace(qv,"$&/")+"/")+e)),t.push(o)),1;if(i=0,n=n===""?".":n+":",$v(e))for(var u=0;u<e.length;u++){a=e[u];var f=n+pp(a,u);i+=ac(a,t,r,f,o)}else if(f=Wj(e),typeof f=="function")for(e=f.call(e),u=0;!(a=e.next()).done;)a=a.value,f=n+pp(a,u++),i+=ac(a,t,r,f,o);else if(a==="object")throw t=String(e),Error("Objects are not valid as a React child (found: "+(t==="[object Object]"?"object with keys {"+Object.keys(e).join(", ")+"}":t)+"). If you meant to render a collection of children, use an array instead.");return i}function Nu(e,t,r){if(e==null)return e;var n=[],o=0;return ac(e,n,"","",function(a){return t.call(r,a,o++)}),n}function $j(e){if(e._status===-1){var t=e._result;t=t(),t.then(function(r){(e._status===0||e._status===-1)&&(e._status=1,e._result=r)},function(r){(e._status===0||e._status===-1)&&(e._status=2,e._result=r)}),e._status===-1&&(e._status=0,e._result=t)}if(e._status===1)return e._result.default;throw e._result}var Er={current:null},ic={transition:null},qj={ReactCurrentDispatcher:Er,ReactCurrentBatchConfig:ic,ReactCurrentOwner:Kh};pt.Children={map:Nu,forEach:function(e,t,r){Nu(e,function(){t.apply(this,arguments)},r)},count:function(e){var t=0;return Nu(e,function(){t++}),t},toArray:function(e){return Nu(e,function(t){return t})||[]},only:function(e){if(!Yh(e))throw Error("React.Children.only expected to receive a single React element child.");return e}};pt.Component=al;pt.Fragment=Aj;pt.Profiler=Ij;pt.PureComponent=qh;pt.StrictMode=Mj;pt.Suspense=zj;pt.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=qj;pt.cloneElement=function(e,t,r){if(e==null)throw Error("React.cloneElement(...): The argument must be a React element, but you passed "+e+".");var n=Fx({},e.props),o=e.key,a=e.ref,i=e._owner;if(t!=null){if(t.ref!==void 0&&(a=t.ref,i=Kh.current),t.key!==void 0&&(o=""+t.key),e.type&&e.type.defaultProps)var u=e.type.defaultProps;for(f in t)Vx.call(t,f)&&!Wx.hasOwnProperty(f)&&(n[f]=t[f]===void 0&&u!==void 0?u[f]:t[f])}var f=arguments.length-2;if(f===1)n.children=r;else if(1<f){u=Array(f);for(var g=0;g<f;g++)u[g]=arguments[g+2];n.children=u}return{$$typeof:Ys,type:e.type,key:o,ref:a,props:n,_owner:i}};pt.createContext=function(e){return e={$$typeof:Lj,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null,_defaultValue:null,_globalName:null},e.Provider={$$typeof:Dj,_conLine truncated
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var Gj=A,Kj=Symbol.for("react.element"),Yj=Symbol.for("react.fragment"),Qj=Object.prototype.hasOwnProperty,Xj=Gj.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,Jj={key:!0,ref:!0,__self:!0,__source:!0};function Hx(e,t,r){var n,o={},a=null,i=null;r!==void 0&&(a=""+r),t.key!==void 0&&(a=""+t.key),t.ref!==void 0&&(i=t.ref);for(n in t)Qj.call(t,n)&&!Jj.hasOwnProperty(n)&&(o[n]=t[n]);if(e&&e.defaultProps)for(n in t=e.defaultProps,t)o[n]===void 0&&(o[n]=t[n]);return{$$typeof:Kj,type:e,key:a,ref:i,props:o,_owner:Xj.current}}Rd.Fragment=Yj;Rd.jsx=Hx;Rd.jsxs=Hx;Ix.exports=Rd;var O=Ix.exports;function $x(e,t){return function(){return e.apply(t,arguments)}}const{toString:Zj}=Object.prototype,{getPrototypeOf:Xh}=Object,Ad=(e=>t=>{const r=Zj.call(t);return e[r]||(e[r]=r.slice(8,-1).toLowerCase())})(Object.create(null)),Wn=e=>(e=e.toLowerCase(),t=>Ad(t)===e),Md=e=>t=>typeof t===e,{isArray:il}=Array,ws=Md("undefined");function e2(e){return e!==null&&!ws(e)&&e.constructor!==null&&!ws(e.constructor)&&Qr(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}const qx=Wn("ArrayBuffer");function t2(e){let t;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?t=ArrayBuffer.isView(e):t=e&&e.buffer&&qx(e.buffer),t}const r2=Md("string"),Qr=Md("function"),Gx=Md("number"),Id=e=>e!==null&&typeof e=="object",n2=e=>e===!0||e===!1,lc=e=>{if(Ad(e)!=="object")return!1;const t=Xh(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(Symbol.toStringTag in e)&&!(Symbol.iterator in e)},o2=Wn("Date"),a2=Wn("File"),i2=Wn("Blob"),l2=Wn("FileList"),s2=e=>Id(e)&&Qr(e.pipe),u2=e=>{let t;return e&&(typeof FormData=="function"&&e instanceof FormData||Qr(e.append)&&((t=Ad(e))==="formdata"||t==="object"&&Qr(e.toString)&&e.toString()==="[object FormData]"))},c2=Wn("URLSearchParams"),[d2,f2,p2,h2]=["ReadableStream","Request","Response","Headers"].map(Wn),g2=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function Qs(e,t,{allOwnKeys:r=!1}={}){if(e===null||typeof e>"u")return;let n,o;if(typeof e!="object"&&(e=[e]),il(e))for(n=0,o=e.length;n<o;n++)t.call(null,e[n],n,e);else{const a=r?Object.getOwnPropertyNames(e):Object.keys(e),i=a.length;let u;for(n=0;n<i;n++)u=a[n],t.call(null,e[u],u,e)}}function Kx(e,t){t=t.toLowerCase();const r=Object.keys(e);let n=r.length,o;for(;n-- >0;)if(o=r[n],t===o.toLowerCase())return o;return null}const Aa=(()=>typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:global)(),Yx=e=>!ws(e)&&e!==Aa;function f0(){const{caseless:e}=Yx(this)&&this||{},t={},r=(n,o)=>{const a=e&&Kx(t,o)||o;lc(t[a])&&lc(n)?t[a]=f0(t[a],n):lc(n)?t[a]=f0({},n):il(n)?t[a]=n.slice():t[a]=n};for(let n=0,o=arguments.length;n<o;n++)arguments[n]&&Qs(arguments[n],r);return t}const m2=(e,t,r,{allOwnKeys:n}={})=>(Qs(t,(o,a)=>{r&&Qr(o)?e[a]=$x(o,r):e[a]=o},{allOwnKeys:n}),e),b2=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),v2=(e,t,r,n)=>{e.prototype=Object.create(t.prototype,n),e.prototype.constructor=e,Object.defineProperty(e,"super",{value:t.prototype}),r&&Object.assign(e.prototype,r)},y2=(e,t,r,n)=>{let o,a,i;const u={};if(t=t||{},e==null)return t;do{for(o=Object.getOwnPropertyNames(e),a=o.length;a-- >0;)i=o[a],(!n||n(i,e,t))&&!u[i]&&(t[i]=e[i],u[i]=!0);e=r!==!1&&Xh(e)}while(e&&(!r||r(e,t))&&e!==Object.prototype);return t},w2=(e,t,r)=>{e=String(e),(r===void 0||r>e.length)&&(r=e.length),r-=t.length;const n=e.indexOf(t,r);return n!==-1&&n===r},x2=e=>{if(!e)return null;if(il(e))return e;let t=e.length;if(!Gx(t))return null;const r=new Array(t);for(;t-- >0;)r[t]=e[t];return r},_2=(e=>t=>e&&t instanceof e)(typeof Uint8Array<"u"&&Xh(Uint8Array)),C2=(e,t)=>{const n=(e&&e[Symbol.iterator]).call(e);let o;for(;(o=n.next())&&!o.done;){const a=o.value;t.call(e,a[0],a[1])}},T2=(e,t)=>{let r;const n=[];for(;(r=e.exec(t))!==null;)n.push(r);return n},S2=Wn("HTMLFormElement"),O2=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(r,n,o){return n.toUpperCase()+o}),Gv=(({hasOwnProperty:e})=>(t,r)=>e.call(t,r))(Object.prototype),P2=Wn("RegExp"),Qx=(e,t)=>{const r=Object.getOwnPropertyDescriptors(e),n={};Qs(r,(o,a)=>{let i;(i=t(o,a,e))!==!1&&(n[a]=i||o)}),Object.defineProperties(e,n)},k2=e=>{Qx(e,(t,r)=>{if(Qr(e)&&["arguments","caller","callee"].indexOf(r)!==-1)return!1;const n=e[r];if(Qr(n)){if(t.enumerable=!1,"writable"in t){t.writable=!1;return}t.set||(t.set=()=>{throw Error("Can not rewrite read-only method '"+r+"'")})}})},E2=(e,t)=>{const r={},n=o=>{o.forEach(a=>{r[a]=!0})};return il(e)?n(e):n(String(e).split(t)),r},N2=()=>{},j2=(e,t)=>e!=null&&Number.isFinite(e=+e)?e:t,hp="abcdefghijklmnopqrstuvwxyz",Kv="0123456789",Xx={DIGIT:Kv,ALPHA:hp,ALPHA_DIGIT:hp+hp.toUpperCase()+Kv},R2=(e=16,t=Xx.ALPHA_DIGIT)=>{let r="";const{length:n}=t;for(;e--;)r+=t[Math.random()*n|0];return r};function A2(e){return!!(e&&Qr(e.append)&&e[Symbol.toStringTag]==="FormData"&&e[Symbol.iterator])}const M2=e=>{const t=new Array(10),r=(n,o)=>{if(Id(n)){if(t.indexOf(n)>=0)return;if(!("toJSON"in n)){t[o]=n;const a=iLine truncated
`).forEach(function(i){o=i.indexOf(":"),r=i.substring(0,o).trim().toLowerCase(),n=i.substring(o+1).trim(),!(!r||t[r]&&tR[r])&&(r==="set-cookie"?t[r]?t[r].push(n):t[r]=[n]:t[r]=t[r]?t[r]+", "+n:n)}),t},Jv=Symbol("internals");function zl(e){return e&&String(e).trim().toLowerCase()}function sc(e){return e===!1||e==null?e:me.isArray(e)?e.map(sc):String(e)}function nR(e){const t=Object.create(null),r=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let n;for(;n=r.exec(e);)t[n[1]]=n[2];return t}const oR=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function gp(e,t,r,n,o){if(me.isFunction(n))return n.call(this,t,r);if(o&&(t=r),!!me.isString(t)){if(me.isString(n))return t.indexOf(n)!==-1;if(me.isRegExp(n))return n.test(t)}}function aR(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(t,r,n)=>r.toUpperCase()+n)}function iR(e,t){const r=me.toCamelCase(" "+t);["get","set","has"].forEach(n=>{Object.defineProperty(e,n+r,{value:function(o,a,i){return this[n].call(this,t,o,a,i)},configurable:!0})})}class Ld{constructor(t){t&&this.set(t)}set(t,r,n){const o=this;function a(u,f,g){const b=zl(f);if(!b)throw new Error("header name must be a non-empty string");const _=me.findKey(o,b);(!_||o[_]===void 0||g===!0||g===void 0&&o[_]!==!1)&&(o[_||f]=sc(u))}const i=(u,f)=>me.forEach(u,(g,b)=>a(g,b,f));if(me.isPlainObject(t)||t instanceof this.constructor)i(t,r);else if(me.isString(t)&&(t=t.trim())&&!oR(t))i(rR(t),r);else if(me.isHeaders(t))for(const[u,f]of t.entries())a(f,u,n);else t!=null&&a(r,t,n);return this}get(t,r){if(t=zl(t),t){const n=me.findKey(this,t);if(n){const o=this[n];if(!r)return o;if(r===!0)return nR(o);if(me.isFunction(r))return r.call(this,o,n);if(me.isRegExp(r))return r.exec(o);throw new TypeError("parser must be boolean|regexp|function")}}}has(t,r){if(t=zl(t),t){const n=me.findKey(this,t);return!!(n&&this[n]!==void 0&&(!r||gp(this,this[n],n,r)))}return!1}delete(t,r){const n=this;let o=!1;function a(i){if(i=zl(i),i){const u=me.findKey(n,i);u&&(!r||gp(n,n[u],u,r))&&(delete n[u],o=!0)}}return me.isArray(t)?t.forEach(a):a(t),o}clear(t){const r=Object.keys(this);let n=r.length,o=!1;for(;n--;){const a=r[n];(!t||gp(this,this[a],a,t,!0))&&(delete this[a],o=!0)}return o}normalize(t){const r=this,n={};return me.forEach(this,(o,a)=>{const i=me.findKey(n,a);if(i){r[i]=sc(o),delete r[a];return}const u=t?aR(a):String(a).trim();u!==a&&delete r[a],r[u]=sc(o),n[u]=!0}),this}concat(...t){return this.constructor.concat(this,...t)}toJSON(t){const r=Object.create(null);return me.forEach(this,(n,o)=>{n!=null&&n!==!1&&(r[o]=t&&me.isArray(n)?n.join(", "):n)}),r}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([t,r])=>t+": "+r).join(`
`)}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(t){return t instanceof this?t:new this(t)}static concat(t,...r){const n=new this(t);return r.forEach(o=>n.set(o)),n}static accessor(t){const n=(this[Jv]=this[Jv]={accessors:{}}).accessors,o=this.prototype;function a(i){const u=zl(i);n[u]||(iR(o,i),n[u]=!0)}return me.isArray(t)?t.forEach(a):a(t),this}}Ld.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);me.reduceDescriptors(Ld.prototype,({value:e},t)=>{let r=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(n){this[r]=n}}});me.freezeMethods(Ld);const An=Ld;function mp(e,t){const r=this||tg,n=t||r,o=An.from(n.headers);let a=n.data;return me.forEach(e,function(u){a=u.call(r,a,o.normalize(),t?t.status:void 0)}),o.normalize(),a}function i_(e){return!!(e&&e.__CANCEL__)}function ll(e,t,r){at.call(this,e??"canceled",at.ERR_CANCELED,t,r),this.name="CanceledError"}me.inherits(ll,at,{__CANCEL__:!0});function l_(e,t,r){const n=r.config.validateStatus;!r.status||!n||n(r.status)?e(r):t(new at("Request failed with status code "+r.status,[at.ERR_BAD_REQUEST,at.ERR_BAD_RESPONSE][Math.floor(r.status/100)-4],r.config,r.request,r))}function lR(e){const t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||""}function sR(e,t){e=e||10;const r=new Array(e),n=new Array(e);let o=0,a=0,i;return t=t!==void 0?t:1e3,function(f){const g=Date.now(),b=n[a];i||(i=g),r[o]=f,n[o]=g;let _=a,S=0;for(;_!==o;)S+=r[_++],_=_%e;if(o=(o+1)%e,o===a&&(a=(a+1)%e),g-i<t)return;const T=b&&g-b;return T?Math.round(S*1e3/T):void 0}}function uR(e,t){let r=0,n=1e3/t,o,a;const i=(g,b=Date.now())=>{r=b,o=null,a&&(clearTimeout(a),a=null),e.apply(null,g)};return[(...g)=>{const b=Date.now(),_=b-r;_>=n?i(g,b):(o=g,a||(a=setTimeout(()=>{a=null,i(o)},n-_)))},()=>o&&i(o)]}const Dc=(e,t,r=3)=>{let n=0;const o=sR(50,250);return uR(a=>{const i=a.loaded,u=a.lengthComputable?a.total:void 0,f=i-n,g=o(f),b=i<=u;n=i;const _={loaded:i,total:u,progress:u?i/u:void 0,bytes:f,rate:g||void 0,estimated:g&&u&&b?(u-i)/g:void 0,event:a,lengthComputable:u!=null,[t?"download":"upload"]:!0};e(_)},r)},Zv=(e,t)=>{const r=e!=null;return[n=>t[0]({lengthComputable:r,total:e,loaded:n}),t[1]]},ey=e=>(...t)=>me.asap(()=>e(...t)),cR=Xr.hasStandardBrowserEnv?function(){const t=Xr.navigator&&/(msie|trident)/i.test(Xr.navigator.userAgent),r=document.createElement("a");let n;function o(a){let i=a;return t&&(r.setAttribute("href",i),i=r.href),r.setAttribute("href",i),{href:r.href,protocol:r.protocol?r.protocol.replace(/:$/,""):"",host:r.host,search:r.search?r.search.replace(/^\?/,""):"",hash:r.hash?r.hash.replace(/^#/,""):"",hostname:r.hostname,port:r.port,pathname:r.pathname.charAt(0)==="/"?r.pathname:"/"+r.pathname}}return n=o(window.location.href),function(i){const u=me.isString(i)?o(i):i;return u.protocol===n.protocol&&u.host===n.host}}():function(){return function(){return!0}}(),dR=Xr.hasStandardBrowserEnv?{write(e,t,r,n,o,a){const i=[e+"="+encodeURIComponent(t)];me.isNumber(r)&&i.push("expires="+new Date(r).toGMTString()),me.isString(n)&&i.push("path="+n),me.isString(o)&&i.push("domain="+o),a===!0&&i.push("secure"),document.cookie=i.join("; ")},read(e){const t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove(e){this.write(e,"",Date.now()-864e5)}}:{write(){},read(){return null},remove(){}};function fR(e){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}function pR(e,t){return t?e.replace(/\/?\/$/,"")+"/"+t.replace(/^\/+/,""):e}function s_(e,t){return e&&!fR(t)?pR(e,t):t}const ty=e=>e instanceof An?{...e}:e;function Va(e,t){t=t||{};const r={};function n(g,b,_){return me.isPlainObject(g)&&me.isPlainObject(b)?me.merge.call({caseless:_},g,b):me.isPlainObject(b)?me.merge({},b):me.isArray(b)?b.slice():b}function o(g,b,_){if(me.isUndefined(b)){if(!me.isUndefined(g))return n(void 0,g,_)}else return n(g,b,_)}function a(g,b){if(!me.isUndefined(b))return n(void 0,b)}function i(g,b){if(me.isUndefined(b)){if(!me.isUndefined(g))return n(void 0,g)}else return n(void 0,b)}function u(g,b,_){if(_ in t)return n(g,b);if(_ in e)return n(void 0,g)}const f={url:a,method:a,data:a,baseURL:i,transformRequest:i,transformResponse:i,paramsSerializer:i,timeout:i,timeoutMessage:i,withCredentials:i,withXSRFToken:i,adapter:i,responseType:i,xsrfCookieName:i,xsrfHeaderName:i,onUploadProgress:i,onDownloadProgress:i,decompress:i,maxContentLength:i,maxBodyLength:i,beforeRedirect:i,transport:i,httpAgent:i,httpsAgent:i,cancelToken:i,socketPath:i,responseEncoding:i,validateStatus:u,headers:(g,b)=>o(ty(g),ty(b),!0)};return me.forEach(Object.keys(Object.assign({},e,t)),function(b){const _=f[b]||o,S=_(e[b],t[b],b);me.isUndefined(S)&&_!==u||(r[b]=S)}),r}const u_=e=>{const t=Va({},e);let{data:r,withXSRFToken:n,xsrfHeaderName:o,xsrfCookieName:a,headers:i,auth:u}=t;t.headers=i=An.from(i),t.url=n_(s_(t.baseURL,t.url),e.params,e.paramsSerializer),u&&i.set("Authorization","Basic "+btoa((u.username||"")+":"+(u.password?unescape(enco Line truncated
`+a.map(oy).join(`
`):" "+oy(a[0]):"as no adapter specified";throw new at("There is no suitable adapter to dispatch the request "+i,"ERR_NOT_SUPPORT")}return n},adapters:b0};function bp(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new ll(null,e)}function ay(e){return bp(e),e.headers=An.from(e.headers),e.data=mp.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),f_.getAdapter(e.adapter||tg.adapter)(e).then(function(n){return bp(e),n.data=mp.call(e,e.transformResponse,n),n.headers=An.from(n.headers),n},function(n){return i_(n)||(bp(e),n&&n.response&&(n.response.data=mp.call(e,e.transformResponse,n.response),n.response.headers=An.from(n.response.headers))),Promise.reject(n)})}const p_="1.7.5",rg={};["object","boolean","number","function","string","symbol"].forEach((e,t)=>{rg[e]=function(n){return typeof n===e||"a"+(t<1?"n ":" ")+e}});const iy={};rg.transitional=function(t,r,n){function o(a,i){return"[Axios v"+p_+"] Transitional option '"+a+"'"+i+(n?". "+n:"")}return(a,i,u)=>{if(t===!1)throw new at(o(i," has been removed"+(r?" in "+r:"")),at.ERR_DEPRECATED);return r&&!iy[i]&&(iy[i]=!0,console.warn(o(i," has been deprecated since v"+r+" and will be removed in the near future"))),t?t(a,i,u):!0}};function SR(e,t,r){if(typeof e!="object")throw new at("options must be an object",at.ERR_BAD_OPTION_VALUE);const n=Object.keys(e);let o=n.length;for(;o-- >0;){const a=n[o],i=t[a];if(i){const u=e[a],f=u===void 0||i(u,a,e);if(f!==!0)throw new at("option "+a+" must be "+f,at.ERR_BAD_OPTION_VALUE);continue}if(r!==!0)throw new at("Unknown option "+a,at.ERR_BAD_OPTION)}}const v0={assertOptions:SR,validators:rg},Bo=v0.validators;class Fc{constructor(t){this.defaults=t,this.interceptors={request:new Xv,response:new Xv}}async request(t,r){try{return await this._request(t,r)}catch(n){if(n instanceof Error){let o;Error.captureStackTrace?Error.captureStackTrace(o={}):o=new Error;const a=o.stack?o.stack.replace(/^.+\n/,""):"";try{n.stack?a&&!String(n.stack).endsWith(a.replace(/^.+\n.+\n/,""))&&(n.stack+=`
`+a):n.stack=a}catch{}}throw n}}_request(t,r){typeof t=="string"?(r=r||{},r.url=t):r=t||{},r=Va(this.defaults,r);const{transitional:n,paramsSerializer:o,headers:a}=r;n!==void 0&&v0.assertOptions(n,{silentJSONParsing:Bo.transitional(Bo.boolean),forcedJSONParsing:Bo.transitional(Bo.boolean),clarifyTimeoutError:Bo.transitional(Bo.boolean)},!1),o!=null&&(me.isFunction(o)?r.paramsSerializer={serialize:o}:v0.assertOptions(o,{encode:Bo.function,serialize:Bo.function},!0)),r.method=(r.method||this.defaults.method||"get").toLowerCase();let i=a&&me.merge(a.common,a[r.method]);a&&me.forEach(["delete","get","head","post","put","patch","common"],C=>{delete a[C]}),r.headers=An.concat(i,a);const u=[];let f=!0;this.interceptors.request.forEach(function(w){typeof w.runWhen=="function"&&w.runWhen(r)===!1||(f=f&&w.synchronous,u.unshift(w.fulfilled,w.rejected))});const g=[];this.interceptors.response.forEach(function(w){g.push(w.fulfilled,w.rejected)});let b,_=0,S;if(!f){const C=[ay.bind(this),void 0];for(C.unshift.apply(C,u),C.push.apply(C,g),S=C.length,b=Promise.resolve(r);_<S;)b=b.then(C[_++],C[_++]);return b}S=u.length;let T=r;for(_=0;_<S;){const C=u[_++],w=u[_++];try{T=C(T)}catch(m){w.call(this,m);break}}try{b=ay.call(this,T)}catch(C){return Promise.reject(C)}for(_=0,S=g.length;_<S;)b=b.then(g[_++],g[_++]);return b}getUri(t){t=Va(this.defaults,t);const r=s_(t.baseURL,t.url);return n_(r,t.params,t.paramsSerializer)}}me.forEach(["delete","get","head","options"],function(t){Fc.prototype[t]=function(r,n){return this.request(Va(n||{},{method:t,url:r,data:(n||{}).data}))}});me.forEach(["post","put","patch"],function(t){function r(n){return function(a,i,u){return this.request(Va(u||{},{method:t,headers:n?{"Content-Type":"multipart/form-data"}:{},url:a,data:i}))}}Fc.prototype[t]=r(),Fc.prototype[t+"Form"]=r(!0)});const uc=Fc;class ng{constructor(t){if(typeof t!="function")throw new TypeError("executor must be a function.");let r;this.promise=new Promise(function(a){r=a});const n=this;this.promise.then(o=>{if(!n._listeners)return;let a=n._listeners.length;for(;a-- >0;)n._listeners[a](o);n._listeners=null}),this.promise.then=o=>{let a;const i=new Promise(u=>{n.subscribe(u),a=u}).then(o);return i.cancel=function(){n.unsubscribe(a)},i},t(function(a,i,u){n.reason||(n.reason=new ll(a,i,u),r(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(t){if(this.reason){t(this.reason);return}this._listeners?this._listeners.push(t):this._listeners=[t]}unsubscribe(t){if(!this._listeners)return;const r=this._listeners.indexOf(t);r!==-1&&this._listeners.splice(r,1)}static source(){let t;return{token:new ng(function(o){t=o}),cancel:t}}}const OR=ng;function PR(e){return function(r){return e.apply(null,r)}}function kR(e){return me.isObject(e)&&e.isAxiosError===!0}const y0={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(y0).forEach(([e,t])=>{y0[t]=e});const ER=y0;function h_(e){const t=new uc(e),r=$x(uc.prototype.request,t);return me.extend(r,uc.prototype,t,{allOwnKeys:!0}),me.extend(r,t,null,{allOwnKeys:!0}),r.create=function(o){return h_(Va(e,o))},r}const er=h_(tg);er.Axios=uc;er.CanceledError=ll;er.CancelToken=OR;er.isCancel=i_;er.VERSION=p_;er.toFormData=Dd;er.AxiosError=at;er.Cancel=er.CanceledError;er.all=function(t){return Promise.all(t)};er.spread=PR;er.isAxiosError=kR;er.mergeConfig=Va;er.AxiosHeaders=An;er.formToJSON=e=>a_(me.isHTMLForm(e)?new FormData(e):e);er.getAdapter=f_.getAdapter;er.HttpStatusCode=ER;er.default=er;const ct=er;var w0={},g_={exports:{}},rn={},m_={exports:{}},b_={};/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/(function(e){function t(W,z){var H=W.length;W.push(z);e:for(;0<H;){var K=H-1>>>1,Z=W[K];if(0<o(Z,z))W[K]=z,W[H]=Z,H=K;else break e}}function r(W){return W.length===0?null:W[0]}function n(W){if(W.length===0)return null;var z=W[0],H=W.pop();if(H!==z){W[0]=H;e:for(var K=0,Z=W.length,Y=Z>>>1;K<Y;){var re=2*(K+1)-1,ae=W[re],te=re+1,ve=W[te];if(0>o(ae,H))te<Z&&0>o(ve,ae)?(W[K]=ve,W[te]=H,K=te):(W[K]=ae,W[re]=H,K=re);else if(te<Z&&0>o(ve,H))W[K]=ve,W[te]=H,K=te;else break e}}return z}function o(W,z){var H=W.sortIndex-z.sortIndex;return H!==0?H:W.id-z.id}if(typeof performance=="object"&&typeof performance.now=="function"){var a=performance;e.unstable_now=function(){return a.now()}}else{var i=Date,u=i.now();e.unstable_now=function(){return i.now()-u}}var f=[],g=[],b=1,_=null,S=3,T=!1,C=!1,w=!1,m=typeof setTimeout=="function"?setTimeout:null,d=typeof clearTimeout=="function"?clearTimeout:null,l=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function c(W){for(var z=r(g);z!==null;){if(z.callback===null)n(g);else if(z.startTime<=W)n(g),z.sortIndex=z.expirationTime,t(f,z);else break;z=r(g)}}function s(W){if(w=!1,c(W),!C)if(r(f)!==null)C=!0,q(h);else{var z=r(g);z!==null&&Q(s,z.startTime-W)}}function h(W,z){C=!1,w&&(w=!1,d(N),N=-1),T=!0;var H=S;try{for(c(z),_=r(f);_!==null&&(!(_.expirationTime>z)||W&&!j());){var K=_.callback;if(typeof K=="function"){_.callback=null,S=_.priorityLevel;var Z=K(_.expirationTime<=z);z=e.unstable_now(),typeof Z=="function"?_.callback=Z:_===r(f)&&n(f),c(z)}else n(f);_=r(f)}if(_!==null)var Y=!0;else{var re=r(g);re!==null&&Q(s,re.startTime-z),Y=!1}return Y}finally{_=null,S=H,T=!1}}var v=!1,y=null,N=-1,k=5,E=-1;function j(){return!(e.unstable_now()-E<k)}function M(){if(y!==null){var W=e.unstable_now();E=W;var z=!0;try{z=y(!0,W)}finally{z?D():(v=!1,y=null)}}else v=!1}var D;if(typeof l=="function")D=function(){l(M)};else if(typeof MessageChannel<"u"){var L=new MessageChannel,U=L.port2;L.port1.onmessage=M,D=function(){U.postMessage(null)}}else D=function(){m(M,0)};function q(W){y=W,v||(v=!0,D())}function Q(W,z){N=m(function(){W(e.unstable_now())},z)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(W){W.callback=null},e.unstable_continueExecution=function(){C||T||(C=!0,q(h))},e.unstable_forceFrameRate=function(W){0>W||125<W?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):k=0<W?Math.floor(1e3/W):5},e.unstable_getCurrentPriorityLevel=function(){return S},e.unstable_getFirstCallbackNode=function(){return r(f)},e.unstable_next=function(W){switch(S){case 1:case 2:case 3:var z=3;break;default:z=S}var H=S;S=z;try{return W()}finally{S=H}},e.unstable_pauseExecution=function(){},e.unstable_requestPaint=function(){},e.unstable_runWithPriority=function(W,z){switch(W){case 1:case 2:case 3:case 4:case 5:break;default:W=3}var H=S;S=W;try{return z()}finally{S=H}},e.unstable_scheduleCallback=function(W,z,H){var K=e.unstable_now();switch(typeof H=="object"&&H!==null?(H=H.delay,H=typeof H=="number"&&0<H?K+H:K):H=K,W){case 1:var Z=-1;break;case 2:Z=250;break;case 5:Z=1073741823;break;case 4:Z=1e4;break;default:Z=5e3}return Z=H+Z,W={id:b++,callback:z,priorityLevel:W,startTime:H,expirationTime:Z,sortIndex:-1},H>K?(W.sortIndex=H,t(g,W),r(f)===null&&W===r(g)&&(w?(d(N),N=-1):w=!0,Q(s,H-K))):(W.sortIndex=Z,t(f,W),C||T||(C=!0,q(h))),W},e.unstable_shouldYield=j,e.unstable_wrapCallback=function(W){var z=S;return function(){var H=S;S=z;try{return W.apply(this,arguments)}finally{S=H}}}})(b_);m_.exports=b_;var NR=m_.exports;/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var v_=A,en=NR;function Ce(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,r=1;r<arguments.length;r++)t+="&args[]="+encodeURIComponent(arguments[r]);return"Minified React error #"+e+"; visit "+t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}var y_=new Set,xs={};function Xa(e,t){Ui(e,t),Ui(e+"Capture",t)}function Ui(e,t){for(xs[e]=t,e=0;e<t.length;e++)y_.add(t[e])}var vo=!(typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),x0=Object.prototype.hasOwnProperty,jR=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,ly={},sy={};function RR(e){return x0.call(sy,e)?!0:x0.call(ly,e)?!1:jR.test(e)?sy[e]=!0:(ly[e]=!0,!1)}function AR(e,t,r,n){if(r!==null&&r.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return n?!1:r!==null?!r.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function MR(e,t,r,n){if(t===null||typeof t>"u"||AR(e,t,r,n))return!0;if(n)return!1;if(r!==null)switch(r.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function Nr(e,t,r,n,o,a,i){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=n,this.attributeNamespace=o,this.mustUseProperty=r,this.propertyName=e,this.type=t,this.sanitizeURL=a,this.removeEmptyString=i}var hr={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){hr[e]=new Nr(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];hr[t]=new Nr(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){hr[e]=new Nr(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){hr[e]=new Nr(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){hr[e]=new Nr(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){hr[e]=new Nr(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){hr[e]=new Nr(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){hr[e]=new Nr(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){hr[e]=new Nr(e,5,!1,e.toLowerCase(),null,!1,!1)});var og=/[\-:]([a-z])/g;function ag(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(og,ag);hr[t]=new Nr(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(og,ag);hr[t]=new Nr(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(og,ag);hr[t]=new Nr(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){hr[e]=new Nr(e,1,!1,e.toLowerCase(),null,!1,!1)});hr.xlinkHref=new Nr("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){hr[e]=new Nr(e,1,!1,e.toLowerCase(),null,!0,!0)});function ig(e,t,r,n){var o=hr.hasOwnProperty(t)?hr[t]:null;(o!==null?o.type!==0:n||!(2<t.length)||t[0]!=="o"&&t[0]!== Line truncated
`+vp+e}var yp=!1;function wp(e,t){if(!e||yp)return"";yp=!0;var r=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{if(t)if(t=function(){throw Error()},Object.defineProperty(t.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(t,[])}catch(g){var n=g}Reflect.construct(e,[],t)}else{try{t.call()}catch(g){n=g}e.call(t.prototype)}else{try{throw Error()}catch(g){n=g}e()}}catch(g){if(g&&n&&typeof g.stack=="string"){for(var o=g.stack.split(`
`),a=n.stack.split(`
`),i=o.length-1,u=a.length-1;1<=i&&0<=u&&o[i]!==a[u];)u--;for(;1<=i&&0<=u;i--,u--)if(o[i]!==a[u]){if(i!==1||u!==1)do if(i--,u--,0>u||o[i]!==a[u]){var f=`
`+o[i].replace(" at new "," at ");return e.displayName&&f.includes("<anonymous>")&&(f=f.replace("<anonymous>",e.displayName)),f}while(1<=i&&0<=u);break}}}finally{yp=!1,Error.prepareStackTrace=r}return(e=e?e.displayName||e.name:"")?Jl(e):""}function IR(e){switch(e.tag){case 5:return Jl(e.type);case 16:return Jl("Lazy");case 13:return Jl("Suspense");case 19:return Jl("SuspenseList");case 0:case 2:case 15:return e=wp(e.type,!1),e;case 11:return e=wp(e.type.render,!1),e;case 1:return e=wp(e.type,!0),e;default:return""}}function S0(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case yi:return"Fragment";case vi:return"Portal";case _0:return"Profiler";case lg:return"StrictMode";case C0:return"Suspense";case T0:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case x_:return(e.displayName||"Context")+".Consumer";case w_:return(e._context.displayName||"Context")+".Provider";case sg:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case ug:return t=e.displayName||null,t!==null?t:S0(e.type)||"Memo";case Ho:t=e._payload,e=e._init;try{return S0(e(t))}catch{}}return null}function DR(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return S0(t);case 8:return t===lg?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function ca(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function C_(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function LR(e){var t=C_(e)?"checked":"value",r=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),n=""+e[t];if(!e.hasOwnProperty(t)&&typeof r<"u"&&typeof r.get=="function"&&typeof r.set=="function"){var o=r.get,a=r.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return o.call(this)},set:function(i){n=""+i,a.call(this,i)}}),Object.defineProperty(e,t,{enumerable:r.enumerable}),{getValue:function(){return n},setValue:function(i){n=""+i},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Ru(e){e._valueTracker||(e._valueTracker=LR(e))}function T_(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var r=t.getValue(),n="";return e&&(n=C_(e)?e.checked?"true":"false":e.value),e=n,e!==r?(t.setValue(e),!0):!1}function zc(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function O0(e,t){var r=t.checked;return Wt({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:r??e._wrapperState.initialChecked})}function cy(e,t){var r=t.defaultValue==null?"":t.defaultValue,n=t.checked!=null?t.checked:t.defaultChecked;r=ca(t.value!=null?t.value:r),e._wrapperState={initialChecked:n,initialValue:r,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function S_(e,t){t=t.checked,t!=null&&ig(e,"checked",t,!1)}function P0(e,t){S_(e,t);var r=ca(t.value),n=t.type;if(r!=null)n==="number"?(r===0&&e.value===""||e.value!=r)&&(e.value=""+r):e.value!==""+r&&(e.value=""+r);else if(n==="submit"||n==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?k0(e,t.type,r):t.hasOwnProperty("defaultValue")&&k0(e,t.type,ca(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function dy(e,t,r){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var n=t.type;if(!(n!=="submit"&&n!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,r||t===e.value||(e.value=t),e.defaultValue=t}r=e.name,r!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,r!==""&&(e.name=r)}function k0(e,t,r){(t!=="number"||zc(e.ownerDocument)!==e)&&(r==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+r&&(e.defaultValue=""+r))}var Zl=Array.isArray;function Ri(e,t,r,n){if(e=e.options,t){t={};for(var o=0;o<r.length;o++)t["$"+r[o]]=!0;for(r=0;r<e.length;r++)o=t.hasOwnProperty("$"+e[r].value),e[r].selected!==o&&(e[r].selected=o),o&&n&&(e[r].defaultSelected=!0)}else{for(r=""+ca(r),t=null,o=0;o<e.length;o++){if(e[o].value===r){e[o].selected=!0,n&&(e[o].defaultSelected=!0);return}t!==null||e[o].disabled||(t=e[o])}t!==null&&(t.selected=!0)}}function E0(e,t){if(t.dan Line truncated
`).replace(GA,"")}function Bu(e,t,r){if(t=My(t),My(e)!==t&&r)throw Error(Ce(425))}function Gc(){}var W0=null,U0=null;function H0(e,t){return e==="textarea"||e==="noscript"||typeof t.children=="string"||typeof t.children=="number"||typeof t.dangerouslySetInnerHTML=="object"&&t.dangerouslySetInnerHTML!==null&&t.dangerouslySetInnerHTML.__html!=null}var $0=typeof setTimeout=="function"?setTimeout:void 0,KA=typeof clearTimeout=="function"?clearTimeout:void 0,Iy=typeof Promise=="function"?Promise:void 0,YA=typeof queueMicrotask=="function"?queueMicrotask:typeof Iy<"u"?function(e){return Iy.resolve(null).then(e).catch(QA)}:$0;function QA(e){setTimeout(function(){throw e})}function Ap(e,t){var r=t,n=0;do{var o=r.nextSibling;if(e.removeChild(r),o&&o.nodeType===8)if(r=o.data,r==="/$"){if(n===0){e.removeChild(o),Os(t);return}n--}else r!=="$"&&r!=="$?"&&r!=="$!"||n++;r=o}while(r);Os(t)}function ra(e){for(;e!=null;e=e.nextSibling){var t=e.nodeType;if(t===1||t===3)break;if(t===8){if(t=e.data,t==="$"||t==="$!"||t==="$?")break;if(t==="/$")return null}}return e}function Dy(e){e=e.previousSibling;for(var t=0;e;){if(e.nodeType===8){var r=e.data;if(r==="$"||r==="$!"||r==="$?"){if(t===0)return e;t--}else r==="/$"&&t++}e=e.previousSibling}return null}var ul=Math.random().toString(36).slice(2),Xn="__reactFiber$"+ul,js="__reactProps$"+ul,yo="__reactContainer$"+ul,q0="__reactEvents$"+ul,XA="__reactListeners$"+ul,JA="__reactHandles$"+ul;function Ma(e){var t=e[Xn];if(t)return t;for(var r=e.parentNode;r;){if(t=r[yo]||r[Xn]){if(r=t.alternate,t.child!==null||r!==null&&r.child!==null)for(e=Dy(e);e!==null;){if(r=e[Xn])return r;e=Dy(e)}return t}e=r,r=e.parentNode}return null}function Zs(e){return e=e[Xn]||e[yo],!e||e.tag!==5&&e.tag!==6&&e.tag!==13&&e.tag!==3?null:e}function Ci(e){if(e.tag===5||e.tag===6)return e.stateNode;throw Error(Ce(33))}function Ud(e){return e[js]||null}var G0=[],Ti=-1;function ha(e){return{current:e}}function jt(e){0>Ti||(e.current=G0[Ti],G0[Ti]=null,Ti--)}function Pt(e,t){Ti++,G0[Ti]=e.current,e.current=t}var da={},Cr=ha(da),zr=ha(!1),Wa=da;function Hi(e,t){var r=e.type.contextTypes;if(!r)return da;var n=e.stateNode;if(n&&n.__reactInternalMemoizedUnmaskedChildContext===t)return n.__reactInternalMemoizedMaskedChildContext;var o={},a;for(a in r)o[a]=t[a];return n&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=o),o}function Br(e){return e=e.childContextTypes,e!=null}function Kc(){jt(zr),jt(Cr)}function Ly(e,t,r){if(Cr.current!==da)throw Error(Ce(168));Pt(Cr,t),Pt(zr,r)}function p1(e,t,r){var n=e.stateNode;if(t=t.childContextTypes,typeof n.getChildContext!="function")return r;n=n.getChildContext();for(var o in n)if(!(o in t))throw Error(Ce(108,DR(e)||"Unknown",o));return Wt({},r,n)}function Yc(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||da,Wa=Cr.current,Pt(Cr,e),Pt(zr,zr.current),!0}function Fy(e,t,r){var n=e.stateNode;if(!n)throw Error(Ce(169));r?(e=p1(e,t,Wa),n.__reactInternalMemoizedMergedChildContext=e,jt(zr),jt(Cr),Pt(Cr,e)):jt(zr),Pt(zr,r)}var fo=null,Hd=!1,Mp=!1;function h1(e){fo===null?fo=[e]:fo.push(e)}function ZA(e){Hd=!0,h1(e)}function ga(){if(!Mp&&fo!==null){Mp=!0;var e=0,t=_t;try{var r=fo;for(_t=1;e<r.length;e++){var n=r[e];do n=n(!0);while(n!==null)}fo=null,Hd=!1}catch(o){throw fo!==null&&(fo=fo.slice(e+1)),z_(dg,ga),o}finally{_t=t,Mp=!1}}return null}var Si=[],Oi=0,Qc=null,Xc=0,fn=[],pn=0,Ua=null,po=1,ho="";function ja(e,t){Si[Oi++]=Xc,Si[Oi++]=Qc,Qc=e,Xc=t}function g1(e,t,r){fn[pn++]=po,fn[pn++]=ho,fn[pn++]=Ua,Ua=e;var n=po;e=ho;var o=32-Mn(n)-1;n&=~(1<<o),r+=1;var a=32-Mn(t)+o;if(30<a){var i=o-o%5;a=(n&(1<<i)-1).toString(32),n>>=i,o-=i,po=1<<32-Mn(t)+o|r<<o|n,ho=a+e}else po=1<<a|r<<o|n,ho=e}function wg(e){e.return!==null&&(ja(e,1),g1(e,1,0))}function xg(e){for(;e===Qc;)Qc=Si[--Oi],Si[Oi]=null,Xc=Si[--Oi],Si[Oi]=null;for(;e===Ua;)Ua=fn[--pn],fn[pn]=null,ho=fn[--pn],fn[pn]=null,po=fn[--pn],fn[pn]=null}var Jr=null,Yr=null,Dt=!1,Nn=null;function m1(e,t){var r=gn(5,null,null,0);r.elementType="DELETED",r.stateNode=t,r.return=e,t=e.deletions,t===null?(e.deletions=[r],e.flags|=16):t.push(r)}function zy(e,t){switch(e.tag){case 5:var r=e.type;return t=t.nodeType!==1||r.toLowerCase()!==t.nodeName.toLowerCase()?null:t,t!==null?(e.stateNode=t,Jr=e,Yr=ra(t.firstChild),!0):!1;case 6:return t=e.pendingProps===""||t.nodeType!==3?null:t,t!==null?(e.stateNode=t,Jr=e,Yr=null,!0):!1;case 13:return t=t.nodeType!==8?null:t,t!==null?(r=Ua!==null?{id:po,overflow:ho}:null,e.memoizedState={dehydrated:t,treeContext:r,retryLane:1073741824},r=gn(18,null,null,0),r.stateNode=t,r.return=e,e.child=r,Jr=e,Yr=null,!0):!1;default:return!1}}function K0(e){return(e.mode&1)!==0&&(e.flags&128)===0}function Y0(e){if(Dt){var t=Yr;if(t){var r=t;if(!zy(e,t)){if(K0(e))throw Error(Ce(418));t=ra(r.nextSibling);var n=Jr;t&&zy(e,t)?m1(n,r):(e.flags=e.flags&-4097|2,Dt=!1,Jr=e)}}else{if(K0(e))throw Error(Ce(418));e.flags=e.flags&-4097|2,Dt=!1,Jr=e}}}function By(e){ Line truncated
Error generating stack: `+a.message+`
`+a.stack}return{value:e,source:t,stack:o,digest:null}}function zp(e,t,r){return{value:e,source:null,stack:r??null,digest:t??null}}function Z0(e,t){try{console.error(t.value)}catch(r){setTimeout(function(){throw r})}}var sM=typeof WeakMap=="function"?WeakMap:Map;function H1(e,t,r){r=go(-1,r),r.tag=3,r.payload={element:null};var n=t.value;return r.callback=function(){ad||(ad=!0,uh=n),Z0(e,t)},r}function $1(e,t,r){r=go(-1,r),r.tag=3;var n=e.type.getDerivedStateFromError;if(typeof n=="function"){var o=t.value;r.payload=function(){return n(o)},r.callback=function(){Z0(e,t)}}var a=e.stateNode;return a!==null&&typeof a.componentDidCatch=="function"&&(r.callback=function(){Z0(e,t),typeof n!="function"&&(oa===null?oa=new Set([this]):oa.add(this));var i=t.stack;this.componentDidCatch(t.value,{componentStack:i!==null?i:""})}),r}function Ky(e,t,r){var n=e.pingCache;if(n===null){n=e.pingCache=new sM;var o=new Set;n.set(t,o)}else o=n.get(t),o===void 0&&(o=new Set,n.set(t,o));o.has(r)||(o.add(r),e=_M.bind(null,e,t,r),t.then(e,e))}function Yy(e){do{var t;if((t=e.tag===13)&&(t=e.memoizedState,t=t!==null?t.dehydrated!==null:!0),t)return e;e=e.return}while(e!==null);return null}function Qy(e,t,r,n,o){return e.mode&1?(e.flags|=65536,e.lanes=o,e):(e===t?e.flags|=65536:(e.flags|=128,r.flags|=131072,r.flags&=-52805,r.tag===1&&(r.alternate===null?r.tag=17:(t=go(-1,1),t.tag=2,na(r,t,1))),r.lanes|=1),e)}var uM=To.ReactCurrentOwner,Lr=!1;function Pr(e,t,r,n){t.child=e===null?C1(t,null,r,n):qi(t,e.child,r,n)}function Xy(e,t,r,n,o){r=r.render;var a=t.ref;return Di(t,o),n=Rg(e,t,r,n,a,o),r=Ag(),e!==null&&!Lr?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,xo(e,t,o)):(Dt&&r&&wg(t),t.flags|=1,Pr(e,t,n,o),t.child)}function Jy(e,t,r,n,o){if(e===null){var a=r.type;return typeof a=="function"&&!Ug(a)&&a.defaultProps===void 0&&r.compare===null&&r.defaultProps===void 0?(t.tag=15,t.type=a,q1(e,t,a,n,o)):(e=yc(r.type,null,n,t,t.mode,o),e.ref=t.ref,e.return=t,t.child=e)}if(a=e.child,!(e.lanes&o)){var i=a.memoizedProps;if(r=r.compare,r=r!==null?r:ks,r(i,n)&&e.ref===t.ref)return xo(e,t,o)}return t.flags|=1,e=ia(a,n),e.ref=t.ref,e.return=t,t.child=e}function q1(e,t,r,n,o){if(e!==null){var a=e.memoizedProps;if(ks(a,n)&&e.ref===t.ref)if(Lr=!1,t.pendingProps=n=a,(e.lanes&o)!==0)e.flags&131072&&(Lr=!0);else return t.lanes=e.lanes,xo(e,t,o)}return eh(e,t,r,n,o)}function G1(e,t,r){var n=t.pendingProps,o=n.children,a=e!==null?e.memoizedState:null;if(n.mode==="hidden")if(!(t.mode&1))t.memoizedState={baseLanes:0,cachePool:null,transitions:null},Pt(Ei,Kr),Kr|=r;else{if(!(r&1073741824))return e=a!==null?a.baseLanes|r:r,t.lanes=t.childLanes=1073741824,t.memoizedState={baseLanes:e,cachePool:null,transitions:null},t.updateQueue=null,Pt(Ei,Kr),Kr|=e,null;t.memoizedState={baseLanes:0,cachePool:null,transitions:null},n=a!==null?a.baseLanes:r,Pt(Ei,Kr),Kr|=n}else a!==null?(n=a.baseLanes|r,t.memoizedState=null):n=r,Pt(Ei,Kr),Kr|=n;return Pr(e,t,o,r),t.child}function K1(e,t){var r=t.ref;(e===null&&r!==null||e!==null&&e.ref!==r)&&(t.flags|=512,t.flags|=2097152)}function eh(e,t,r,n,o){var a=Br(r)?Wa:Cr.current;return a=Hi(t,a),Di(t,o),r=Rg(e,t,r,n,a,o),n=Ag(),e!==null&&!Lr?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,xo(e,t,o)):(Dt&&n&&wg(t),t.flags|=1,Pr(e,t,r,o),t.child)}function Zy(e,t,r,n,o){if(Br(r)){var a=!0;Yc(t)}else a=!1;if(Di(t,o),t.stateNode===null)mc(e,t),x1(t,r,n),J0(t,r,n,o),n=!0;else if(e===null){var i=t.stateNode,u=t.memoizedProps;i.props=u;var f=i.context,g=r.contextType;typeof g=="object"&&g!==null?g=yn(g):(g=Br(r)?Wa:Cr.current,g=Hi(t,g));var b=r.getDerivedStateFromProps,_=typeof b=="function"||typeof i.getSnapshotBeforeUpdate=="function";_||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(u!==n||f!==g)&&Hy(t,i,n,g),$o=!1;var S=t.memoizedState;i.state=S,ed(t,n,i,o),f=t.memoizedState,u!==n||S!==f||zr.current||$o?(typeof b=="function"&&(X0(t,r,b,n),f=t.memoizedState),(u=$o||Uy(t,r,u,n,S,f,g))?(_||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount()),typeof i.componentDidMount=="function"&&(t.flags|=4194308)):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),t.memoizedProps=n,t.memoizedState=f),i.props=n,i.state=f,i.context=g,n=u):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),n=!1)}else{i=t.stateNode,y1(e,t),u=t.memoizedProps,g=t.type===t.elementType?u:Pn(t.type,u),i.props=g,_=t.pendingProps,S=i.context,f=r.contextType,typeof f=="object"&&f!==null?f=yn(f):(f=Br(r)?Wa:Cr.current,f=Hi(t,f));var T=r.getDerivedStateFromProps;(b=typeof T=="function"||typeof i.getSnapshotBeforeUpdate=="function")||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(u!==_||S!==f)&&Hy(t,i,n,f),$o=!1,S=t.memoizedState,i.state=S,ed(t,n,i,o);var C=t.memoizedS Line truncated
* @remix-run/router v1.10.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/function Fs(){return Fs=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},Fs.apply(this,arguments)}var Xo;(function(e){e.Pop="POP",e.Push="PUSH",e.Replace="REPLACE"})(Xo||(Xo={}));const pw="popstate";function AM(e){e===void 0&&(e={});function t(n,o){let{pathname:a,search:i,hash:u}=n.location;return ph("",{pathname:a,search:i,hash:u},o.state&&o.state.usr||null,o.state&&o.state.key||"default")}function r(n,o){return typeof o=="string"?o:ud(o)}return IM(t,r,null,e)}function Vt(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function Kg(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function MM(){return Math.random().toString(36).substr(2,8)}function hw(e,t){return{usr:e.state,key:e.key,idx:t}}function ph(e,t,r,n){return r===void 0&&(r=null),Fs({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?cl(t):t,{state:r,key:t&&t.key||n||MM()})}function ud(e){let{pathname:t="/",search:r="",hash:n=""}=e;return r&&r!=="?"&&(t+=r.charAt(0)==="?"?r:"?"+r),n&&n!=="#"&&(t+=n.charAt(0)==="#"?n:"#"+n),t}function cl(e){let t={};if(e){let r=e.indexOf("#");r>=0&&(t.hash=e.substr(r),e=e.substr(0,r));let n=e.indexOf("?");n>=0&&(t.search=e.substr(n),e=e.substr(0,n)),e&&(t.pathname=e)}return t}function IM(e,t,r,n){n===void 0&&(n={});let{window:o=document.defaultView,v5Compat:a=!1}=n,i=o.history,u=Xo.Pop,f=null,g=b();g==null&&(g=0,i.replaceState(Fs({},i.state,{idx:g}),""));function b(){return(i.state||{idx:null}).idx}function _(){u=Xo.Pop;let m=b(),d=m==null?null:m-g;g=m,f&&f({action:u,location:w.location,delta:d})}function S(m,d){u=Xo.Push;let l=ph(w.location,m,d);r&&r(l,m),g=b()+1;let c=hw(l,g),s=w.createHref(l);try{i.pushState(c,"",s)}catch(h){if(h instanceof DOMException&&h.name==="DataCloneError")throw h;o.location.assign(s)}a&&f&&f({action:u,location:w.location,delta:1})}function T(m,d){u=Xo.Replace;let l=ph(w.location,m,d);r&&r(l,m),g=b();let c=hw(l,g),s=w.createHref(l);i.replaceState(c,"",s),a&&f&&f({action:u,location:w.location,delta:0})}function C(m){let d=o.location.origin!=="null"?o.location.origin:o.location.href,l=typeof m=="string"?m:ud(m);return Vt(d,"No window.location.(origin|href) available to create URL for href: "+l),new URL(l,d)}let w={get action(){return u},get location(){return e(o,i)},listen(m){if(f)throw new Error("A history only accepts one active listener");return o.addEventListener(pw,_),f=m,()=>{o.removeEventListener(pw,_),f=null}},createHref(m){return t(o,m)},createURL:C,encodeLocation(m){let d=C(m);return{pathname:d.pathname,search:d.search,hash:d.hash}},push:S,replace:T,go(m){return i.go(m)}};return w}var gw;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})(gw||(gw={}));function DM(e,t,r){r===void 0&&(r="/");let n=typeof t=="string"?cl(t):t,o=zs(n.pathname||"/",r);if(o==null)return null;let a=v5(e);LM(a);let i=null;for(let u=0;i==null&&u<a.length;++u)i=qM(a[u],KM(o));return i}function v5(e,t,r,n){t===void 0&&(t=[]),r===void 0&&(r=[]),n===void 0&&(n="");let o=(a,i,u)=>{let f={relativePath:u===void 0?a.path||"":u,caseSensitive:a.caseSensitive===!0,childrenIndex:i,route:a};f.relativePath.startsWith("/")&&(Vt(f.relativePath.startsWith(n),'Absolute route path "'+f.relativePath+'" nested under path '+('"'+n+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),f.relativePath=f.relativePath.slice(n.length));let g=la([n,f.relativePath]),b=r.concat(f);a.children&&a.children.length>0&&(Vt(a.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+g+'".')),v5(a.children,t,b,g)),!(a.path==null&&!a.index)&&t.push({path:g,score:HM(g,a.index),routesMeta:b})};return e.forEach((a,i)=>{var u;if(a.path===""||!((u=a.path)!=null&&u.includes("?")))o(a,i);else for(let f of y5(a.path))o(a,i,f)}),t}function y5(e){let t=e.split("/");if(t.length===0)return[];let[r,...n]=t,o=r.endsWith("?"),a=r.replace(/\?$/,"");if(n.length===0)return o?[a,""]:[a];let i=y5(n.join("/")),u=[];return u.push(...i.map(f=>f===""?a:[a,f].join("/"))),o&&u.push(...i),u.map(f=>e.startsWith("/")&&f===""?"/":f)}function LM(e){e.sort((t,r)=>t.score!==r.score?r.score-t.score:$M(t.routesMeta.map(n=>n.childrenIndex),r.routesMeta.map(n=>n.childrenIndex)))}const FM=/^:\w+$/,zM=3,BM=2,VM=1,WM=10,UM=-2,mw=e=>e==="*";function HM(e,t){let r=e.split("/"),n=r.length;return r.some(mw)&&(n+=UM),t&&(n+=BM),r.filter(o=>!mw(o)).reduce((o,a)=>o+(FM.test(a)?zM:a===""?VM:WM),n)}function $M(e,t){return e.length===t.length&&e.slice(0,-1).every((n,o)=>n===t[o])?e[e.length-1]-t[t.length-1]:0}function qM(e,t){let{routesMeta:r}=e,n={},o="/",a=[];for(let i=0;i<r.length;++i){let u=r[i],f=i===r.length-1,g=o==="/"?t:t.slice(o.length)||"/",b=hh({path:u.relativePath,caseSensitive:u.caseSensitive,end:f},g);if(!b)return null;Object.assign(n,b.paLine truncated
* React Router v6.17.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/function cd(){return cd=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},cd.apply(this,arguments)}const ef=A.createContext(null),x5=A.createContext(null),Za=A.createContext(null),tf=A.createContext(null),So=A.createContext({outlet:null,matches:[],isDataRoute:!1}),_5=A.createContext(null);function nI(e,t){let{relative:r}=t===void 0?{}:t;dl()||Vt(!1);let{basename:n,navigator:o}=A.useContext(Za),{hash:a,pathname:i,search:u}=rf(e,{relative:r}),f=i;return n!=="/"&&(f=i==="/"?n:la([n,i])),o.createHref({pathname:f,search:u,hash:a})}function dl(){return A.useContext(tf)!=null}function ma(){return dl()||Vt(!1),A.useContext(tf).location}function C5(e){A.useContext(Za).static||A.useLayoutEffect(e)}function fl(){let{isDataRoute:e}=A.useContext(So);return e?bI():oI()}function oI(){dl()||Vt(!1);let e=A.useContext(ef),{basename:t,navigator:r}=A.useContext(Za),{matches:n}=A.useContext(So),{pathname:o}=ma(),a=JSON.stringify(Yg(n).map(f=>f.pathnameBase)),i=A.useRef(!1);return C5(()=>{i.current=!0}),A.useCallback(function(f,g){if(g===void 0&&(g={}),!i.current)return;if(typeof f=="number"){r.go(f);return}let b=Qg(f,JSON.parse(a),o,g.relative==="path");e==null&&t!=="/"&&(b.pathname=b.pathname==="/"?t:la([t,b.pathname])),(g.replace?r.replace:r.push)(b,g.state,g)},[t,r,a,o,e])}function aI(){let{matches:e}=A.useContext(So),t=e[e.length-1];return t?t.params:{}}function rf(e,t){let{relative:r}=t===void 0?{}:t,{matches:n}=A.useContext(So),{pathname:o}=ma(),a=JSON.stringify(Yg(n).map(i=>i.pathnameBase));return A.useMemo(()=>Qg(e,JSON.parse(a),o,r==="path"),[e,a,o,r])}function iI(e,t){return lI(e,t)}function lI(e,t,r){dl()||Vt(!1);let{navigator:n}=A.useContext(Za),{matches:o}=A.useContext(So),a=o[o.length-1],i=a?a.params:{};a&&a.pathname;let u=a?a.pathnameBase:"/";a&&a.route;let f=ma(),g;if(t){var b;let w=typeof t=="string"?cl(t):t;u==="/"||(b=w.pathname)!=null&&b.startsWith(u)||Vt(!1),g=w}else g=f;let _=g.pathname||"/",S=u==="/"?_:_.slice(u.length)||"/",T=DM(e,{pathname:S}),C=fI(T&&T.map(w=>Object.assign({},w,{params:Object.assign({},i,w.params),pathname:la([u,n.encodeLocation?n.encodeLocation(w.pathname).pathname:w.pathname]),pathnameBase:w.pathnameBase==="/"?u:la([u,n.encodeLocation?n.encodeLocation(w.pathnameBase).pathname:w.pathnameBase])})),o,r);return t&&C?A.createElement(tf.Provider,{value:{location:cd({pathname:"/",search:"",hash:"",state:null,key:"default"},g),navigationType:Xo.Pop}},C):C}function sI(){let e=mI(),t=tI(e)?e.status+" "+e.statusText:e instanceof Error?e.message:JSON.stringify(e),r=e instanceof Error?e.stack:null,o={padding:"0.5rem",backgroundColor:"rgba(200,200,200, 0.5)"},a=null;return A.createElement(A.Fragment,null,A.createElement("h2",null,"Unexpected Application Error!"),A.createElement("h3",{style:{fontStyle:"italic"}},t),r?A.createElement("pre",{style:o},r):null,a)}const uI=A.createElement(sI,null);class cI extends A.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,r){return r.location!==t.location||r.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error||r.error,location:r.location,revalidation:t.revalidation||r.revalidation}}componentDidCatch(t,r){console.error("React Router caught the following error during render",t,r)}render(){return this.state.error?A.createElement(So.Provider,{value:this.props.routeContext},A.createElement(_5.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function dI(e){let{routeContext:t,match:r,children:n}=e,o=A.useContext(ef);return o&&o.static&&o.staticContext&&(r.route.errorElement||r.route.ErrorBoundary)&&(o.staticContext._deepestRenderedBoundaryId=r.route.id),A.createElement(So.Provider,{value:t},n)}function fI(e,t,r){var n;if(t===void 0&&(t=[]),r===void 0&&(r=null),e==null){var o;if((o=r)!=null&&o.errors)e=r.matches;else return null}let a=e,i=(n=r)==null?void 0:n.errors;if(i!=null){let u=a.findIndex(f=>f.route.id&&(i==null?void 0:i[f.route.id]));u>=0||Vt(!1),a=a.slice(0,Math.min(a.length,u+1))}return a.reduceRight((u,f,g)=>{let b=f.route.id?i==null?void 0:i[f.route.id]:null,_=null;r&&(_=f.route.errorElement||uI);let S=t.concat(a.slice(0,g+1)),T=()=>{let C;return b?C=_:f.route.Component?C=A.createElement(f.route.Component,null):f.route.element?C=f.route.element:C=u,A.createElement(dI,{match:f,routeContext:{outlet:u,matches:S,isDataRoute:r!=null},children:C})};return r&&(f.route.ErrorBoundary||f.route.errorElement||g===0)?A.createElement(cI,{location:r.location,revalidation:r.revalidation,component:_,error:b,children:T(),routeContext:{outlet:null,matches:S,isDataRoute:!0}}):T()},null)}var T5=function(e){return e.UseBlocker="useBlocker",e.UseRevalidator="useRevalidator",e.UseNavigateStable=Line truncated
* React Router DOM v6.17.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/function fd(){return fd=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},fd.apply(this,arguments)}function O5(e,t){if(e==null)return{};var r={},n=Object.keys(e),o,a;for(a=0;a<n.length;a++)o=n[a],!(t.indexOf(o)>=0)&&(r[o]=e[o]);return r}function wI(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function xI(e,t){return e.button===0&&(!t||t==="_self")&&!wI(e)}const _I=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","unstable_viewTransition"],CI=["aria-current","caseSensitive","className","end","style","to","unstable_viewTransition","children"],TI=A.createContext({isTransitioning:!1}),SI="startTransition",bw=Qh[SI];function OI(e){let{basename:t,children:r,future:n,window:o}=e,a=A.useRef();a.current==null&&(a.current=AM({window:o,v5Compat:!0}));let i=a.current,[u,f]=A.useState({action:i.action,location:i.location}),{v7_startTransition:g}=n||{},b=A.useCallback(_=>{g&&bw?bw(()=>f(_)):f(_)},[f,g]);return A.useLayoutEffect(()=>i.listen(b),[i,b]),A.createElement(yI,{basename:t,children:r,location:u.location,navigationType:u.action,navigator:i})}const PI=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",kI=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Jg=A.forwardRef(function(t,r){let{onClick:n,relative:o,reloadDocument:a,replace:i,state:u,target:f,to:g,preventScrollReset:b,unstable_viewTransition:_}=t,S=O5(t,_I),{basename:T}=A.useContext(Za),C,w=!1;if(typeof g=="string"&&kI.test(g)&&(C=g,PI))try{let c=new URL(window.location.href),s=g.startsWith("//")?new URL(c.protocol+g):new URL(g),h=zs(s.pathname,T);s.origin===c.origin&&h!=null?g=h+s.search+s.hash:w=!0}catch{}let m=nI(g,{relative:o}),d=jI(g,{replace:i,state:u,target:f,preventScrollReset:b,relative:o,unstable_viewTransition:_});function l(c){n&&n(c),c.defaultPrevented||d(c)}return A.createElement("a",fd({},S,{href:C||m,onClick:w||a?n:l,ref:r,target:f}))}),EI=A.forwardRef(function(t,r){let{"aria-current":n="page",caseSensitive:o=!1,className:a="",end:i=!1,style:u,to:f,unstable_viewTransition:g,children:b}=t,_=O5(t,CI),S=rf(f,{relative:_.relative}),T=ma(),C=A.useContext(x5),{navigator:w}=A.useContext(Za),m=C!=null&&RI(S)&&g===!0,d=w.encodeLocation?w.encodeLocation(S).pathname:S.pathname,l=T.pathname,c=C&&C.navigation&&C.navigation.location?C.navigation.location.pathname:null;o||(l=l.toLowerCase(),c=c?c.toLowerCase():null,d=d.toLowerCase());let s=l===d||!i&&l.startsWith(d)&&l.charAt(d.length)==="/",h=c!=null&&(c===d||!i&&c.startsWith(d)&&c.charAt(d.length)==="/"),v={isActive:s,isPending:h,isTransitioning:m},y=s?n:void 0,N;typeof a=="function"?N=a(v):N=[a,s?"active":null,h?"pending":null,m?"transitioning":null].filter(Boolean).join(" ");let k=typeof u=="function"?u(v):u;return A.createElement(Jg,fd({},_,{"aria-current":y,className:N,ref:r,style:k,to:f,unstable_viewTransition:g}),typeof b=="function"?b(v):b)});var mh;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(mh||(mh={}));var vw;(function(e){e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(vw||(vw={}));function NI(e){let t=A.useContext(ef);return t||Vt(!1),t}function jI(e,t){let{target:r,replace:n,state:o,preventScrollReset:a,relative:i,unstable_viewTransition:u}=t===void 0?{}:t,f=fl(),g=ma(),b=rf(e,{relative:i});return A.useCallback(_=>{if(xI(_,r)){_.preventDefault();let S=n!==void 0?n:ud(g)===ud(b);f(e,{replace:S,state:o,preventScrollReset:a,relative:i,unstable_viewTransition:u})}},[g,f,b,n,o,r,e,a,i,u])}function RI(e,t){t===void 0&&(t={});let r=A.useContext(TI);r==null&&Vt(!1);let{basename:n}=NI(mh.useViewTransitionState),o=rf(e,{relative:t.relative});if(!r.isTransitioning)return!1;let a=zs(r.currentLocation.pathname,n)||r.currentLocation.pathname,i=zs(r.nextLocation.pathname,n)||r.nextLocation.pathname;return hh(o.pathname,i)!=null||hh(o.pathname,a)!=null}function AI({title:e,titleId:t,...r},n){return A.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":"true",ref:n,"aria-labelledby":t},r),e?A.createElement("title",{id:t},e):null,A.createElement("path",{fillRule:"evenodd",d:"M3 6.75A.75.75 0 013.75 6h16.5a.75.75 0 010 1.5H3.75A.75.75 0 013 6.75zM3 12a.75.75 0 01.75-.75h16.5a.75.75 0 010 1.5H3.75A.75.75 0 013 12zm0 5.25a.75.75 0 01.75-.75h16.5a.75.75 0 010 1.5H3.75a.75.75 0 01-.75-.75z",clipRule:"evenodd"}))}const MI=A.forwardRef(AI),II=MI;function DI({title:e,titleId:t,...r},n){return A.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":"true",ref:n,"aria-labelledby":t},r),e?A.createElement("title",{id:t},e):null,A.createElement("path",{fillRule:"evenodd",d:"M11.078 2.25c-.917 0-1.69Line truncated
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/(function(e){(function(){var t={}.hasOwnProperty;function r(){for(var n=[],o=0;o<arguments.length;o++){var a=arguments[o];if(a){var i=typeof a;if(i==="string"||i==="number")n.push(a);else if(Array.isArray(a)){if(a.length){var u=r.apply(null,a);u&&n.push(u)}}else if(i==="object"){if(a.toString!==Object.prototype.toString&&!a.toString.toString().includes("[native code]")){n.push(a.toString());continue}for(var f in a)t.call(a,f)&&a[f]&&n.push(f)}}}return n.join(" ")}e.exports?(r.default=r,e.exports=r):window.classNames=r})()})(E5);var Ze=E5.exports,N5={exports:{}},On={};function Zg(){for(var e,t,r=0,n="";r<arguments.length;)(e=arguments[r++])&&(t=j5(e))&&(n&&(n+=" "),n+=t);return n}function j5(e){if(typeof e=="string")return e;for(var t,r="",n=0;n<e.length;n++)e[n]&&(t=j5(e[n]))&&(r&&(r+=" "),r+=t);return r}function bh(){return bh=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},bh.apply(this,arguments)}function a4(e){var t=function(r){var n=r.theme,o=r.prefix,a={nextPart:new Map,validators:[]};return function(i,u){return u?i.map(function(f){return[f[0],f[1].map(function(g){return typeof g=="string"?u+g:typeof g=="object"?Object.fromEntries(Object.entries(g).map(function(b){return[u+b[0],b[1]]})):g})]}):i}(Object.entries(r.classGroups),o).forEach(function(i){vh(i[1],a,i[0],n)}),a}(e);return{getClassGroupId:function(r){var n=r.split("-");return n[0]===""&&n.length!==1&&n.shift(),R5(n,t)||function(o){if(yw.test(o)){var a=yw.exec(o)[1],i=a==null?void 0:a.substring(0,a.indexOf(":"));if(i)return"arbitrary.."+i}}(r)},getConflictingClassGroupIds:function(r){return e.conflictingClassGroups[r]||[]}}}function R5(e,t){var r;if(e.length===0)return t.classGroupId;var n=t.nextPart.get(e[0]),o=n?R5(e.slice(1),n):void 0;if(o)return o;if(t.validators.length!==0){var a=e.join("-");return(r=t.validators.find(function(i){return(0,i.validator)(a)}))==null?void 0:r.classGroupId}}Object.defineProperty(On,"__esModule",{value:!0});var yw=/^\[(.+)\]$/;function vh(e,t,r,n){e.forEach(function(o){if(typeof o!="string"){if(typeof o=="function")return o.isThemeGetter?void vh(o(n),t,r,n):void t.validators.push({validator:o,classGroupId:r});Object.entries(o).forEach(function(a){vh(a[1],ww(t,a[0]),r,n)})}else(o===""?t:ww(t,o)).classGroupId=r})}function ww(e,t){var r=e;return t.split("-").forEach(function(n){r.nextPart.has(n)||r.nextPart.set(n,{nextPart:new Map,validators:[]}),r=r.nextPart.get(n)}),r}function i4(e){if(e<1)return{get:function(){},set:function(){}};var t=0,r=new Map,n=new Map;function o(a,i){r.set(a,i),++t>e&&(t=0,n=r,r=new Map)}return{get:function(a){var i=r.get(a);return i!==void 0?i:(i=n.get(a))!==void 0?(o(a,i),i):void 0},set:function(a,i){r.has(a)?r.set(a,i):o(a,i)}}}function l4(e){var t=e.separator||":";return function(r){for(var n=0,o=[],a=0,i=0;i<r.length;i++){var u=r[i];n===0&&u===t[0]&&(t.length!==1&&r.slice(i,i+t.length)!==t||(o.push(r.slice(a,i)),a=i+t.length)),u==="["?n++:u==="]"&&n--}var f=o.length===0?r:r.substring(a),g=f.startsWith("!");return{modifiers:o,hasImportantModifier:g,baseClassName:g?f.substring(1):f}}}function s4(e){return bh({cache:i4(e.cacheSize),splitModifiers:l4(e)},a4(e))}var u4=/\s+/;function c4(e,t){var r=t.splitModifiers,n=t.getClassGroupId,o=t.getConflictingClassGroupIds,a=new Set;return e.trim().split(u4).map(function(i){var u=r(i),f=u.modifiers,g=u.hasImportantModifier,b=n(u.baseClassName);if(!b)return{isTailwindClass:!1,originalClassName:i};var _=function(S){if(S.length<=1)return S;var T=[],C=[];return S.forEach(function(w){w[0]==="["?(T.push.apply(T,C.sort().concat([w])),C=[]):C.push(w)}),T.push.apply(T,C.sort()),T}(f).join(":");return{isTailwindClass:!0,modifierId:g?_+"!":_,classGroupId:b,originalClassName:i}}).reverse().filter(function(i){if(!i.isTailwindClass)return!0;var u=i.modifierId,f=i.classGroupId,g=u+f;return!a.has(g)&&(a.add(g),o(f).forEach(function(b){return a.add(u+b)}),!0)}).reverse().map(function(i){return i.originalClassName}).join(" ")}function yh(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];var n,o,a,i=u;function u(g){var b=t[0],_=t.slice(1).reduce(function(S,T){return T(S)},b());return n=s4(_),o=n.cache.get,a=n.cache.set,i=f,f(g)}function f(g){var b=o(g);if(b)return b;var _=c4(g,n);return a(g,_),_}return function(){return i(Zg.apply(null,arguments))}}function Nt(e){var t=function(r){return r[e]||[]};return t.isThemeGetter=!0,t}var ba=/^\[(.+)\]$/,d4=/^\d+\/\d+$/,f4=new Set(["px","full","screen"]),p4=/^(\d+)?(xs|sm|md|lg|xl)$/,h4=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh)/,g4=/^-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/;function cn(e){return!Number.isNaN(Number(e))||f4.has(e)||d4.test(e)||qo(e)}function qo(e){var t,r=(t=ba.exec(e))==null?void 0:t[1];return!!r&&(r.startsWith("length:")||h4.test(r))}function A5(e){var t,r=(t=ba.exec(e))==null?void 0:t[1];return!!r&&r.startsWLine truncated
* tabbable 6.2.0
* @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE
*/var aF=["input:not([inert])","select:not([inert])","textarea:not([inert])","a[href]:not([inert])","button:not([inert])","[tabindex]:not(slot):not([inert])","audio[controls]:not([inert])","video[controls]:not([inert])",'[contenteditable]:not([contenteditable="false"]):not([inert])',"details>summary:first-of-type:not([inert])","details:not([inert])"],Mh=aF.join(","),BO=typeof Element>"u",$s=BO?function(){}:Element.prototype.matches||Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector,_d=!BO&&Element.prototype.getRootNode?function(e){var t;return e==null||(t=e.getRootNode)===null||t===void 0?void 0:t.call(e)}:function(e){return e==null?void 0:e.ownerDocument},Cd=function e(t,r){var n;r===void 0&&(r=!0);var o=t==null||(n=t.getAttribute)===null||n===void 0?void 0:n.call(t,"inert"),a=o===""||o==="true",i=a||r&&t&&e(t.parentNode);return i},iF=function(t){var r,n=t==null||(r=t.getAttribute)===null||r===void 0?void 0:r.call(t,"contenteditable");return n===""||n==="true"},lF=function(t,r,n){if(Cd(t))return[];var o=Array.prototype.slice.apply(t.querySelectorAll(Mh));return r&&$s.call(t,Mh)&&o.unshift(t),o=o.filter(n),o},sF=function e(t,r,n){for(var o=[],a=Array.from(t);a.length;){var i=a.shift();if(!Cd(i,!1))if(i.tagName==="SLOT"){var u=i.assignedElements(),f=u.length?u:i.children,g=e(f,!0,n);n.flatten?o.push.apply(o,g):o.push({scopeParent:i,candidates:g})}else{var b=$s.call(i,Mh);b&&n.filter(i)&&(r||!t.includes(i))&&o.push(i);var _=i.shadowRoot||typeof n.getShadowRoot=="function"&&n.getShadowRoot(i),S=!Cd(_,!1)&&(!n.shadowRootFilter||n.shadowRootFilter(i));if(_&&S){var T=e(_===!0?i.children:_.children,!0,n);n.flatten?o.push.apply(o,T):o.push({scopeParent:i,candidates:T})}else a.unshift.apply(a,i.children)}}return o},VO=function(t){return!isNaN(parseInt(t.getAttribute("tabindex"),10))},WO=function(t){if(!t)throw new Error("No node provided");return t.tabIndex<0&&(/^(AUDIO|VIDEO|DETAILS)$/.test(t.tagName)||iF(t))&&!VO(t)?0:t.tabIndex},uF=function(t,r){var n=WO(t);return n<0&&r&&!VO(t)?0:n},cF=function(t,r){return t.tabIndex===r.tabIndex?t.documentOrder-r.documentOrder:t.tabIndex-r.tabIndex},UO=function(t){return t.tagName==="INPUT"},dF=function(t){return UO(t)&&t.type==="hidden"},fF=function(t){var r=t.tagName==="DETAILS"&&Array.prototype.slice.apply(t.children).some(function(n){return n.tagName==="SUMMARY"});return r},pF=function(t,r){for(var n=0;n<t.length;n++)if(t[n].checked&&t[n].form===r)return t[n]},hF=function(t){if(!t.name)return!0;var r=t.form||_d(t),n=function(u){return r.querySelectorAll('input[type="radio"][name="'+u+'"]')},o;if(typeof window<"u"&&typeof window.CSS<"u"&&typeof window.CSS.escape=="function")o=n(window.CSS.escape(t.name));else try{o=n(t.name)}catch(i){return console.error("Looks like you have a radio button with a name attribute containing invalid CSS selector characters and need the CSS.escape polyfill: %s",i.message),!1}var a=pF(o,t.form);return!a||a===t},gF=function(t){return UO(t)&&t.type==="radio"},mF=function(t){return gF(t)&&!hF(t)},bF=function(t){var r,n=t&&_d(t),o=(r=n)===null||r===void 0?void 0:r.host,a=!1;if(n&&n!==t){var i,u,f;for(a=!!((i=o)!==null&&i!==void 0&&(u=i.ownerDocument)!==null&&u!==void 0&&u.contains(o)||t!=null&&(f=t.ownerDocument)!==null&&f!==void 0&&f.contains(t));!a&&o;){var g,b,_;n=_d(o),o=(g=n)===null||g===void 0?void 0:g.host,a=!!((b=o)!==null&&b!==void 0&&(_=b.ownerDocument)!==null&&_!==void 0&&_.contains(o))}}return a},Kw=function(t){var r=t.getBoundingClientRect(),n=r.width,o=r.height;return n===0&&o===0},vF=function(t,r){var n=r.displayCheck,o=r.getShadowRoot;if(getComputedStyle(t).visibility==="hidden")return!0;var a=$s.call(t,"details>summary:first-of-type"),i=a?t.parentElement:t;if($s.call(i,"details:not([open]) *"))return!0;if(!n||n==="full"||n==="legacy-full"){if(typeof o=="function"){for(var u=t;t;){var f=t.parentElement,g=_d(t);if(f&&!f.shadowRoot&&o(f)===!0)return Kw(t);t.assignedSlot?t=t.assignedSlot:!f&&g!==t.ownerDocument?t=g.host:t=f}t=u}if(bF(t))return!t.getClientRects().length;if(n!=="legacy-full")return!0}else if(n==="non-zero-area")return Kw(t);return!1},yF=function(t){if(/^(INPUT|BUTTON|SELECT|TEXTAREA)$/.test(t.tagName))for(var r=t.parentElement;r;){if(r.tagName==="FIELDSET"&&r.disabled){for(var n=0;n<r.children.length;n++){var o=r.children.item(n);if(o.tagName==="LEGEND")return $s.call(r,"fieldset[disabled] *")?!0:!o.contains(t)}return!0}r=r.parentElement}return!1},wF=function(t,r){return!(r.disabled||Cd(r)||dF(r)||vF(r,t)||fF(r)||yF(r))},Yw=function(t,r){return!(mF(r)||WO(r)<0||!wF(t,r))},xF=function(t){var r=parseInt(t.getAttribute("tabindex"),10);return!!(isNaN(r)||r>=0)},_F=function e(t){var r=[],n=[];return t.forEach(function(o,a){var i=!!o.scopeParent,u=i?o.scopeParent:o,f=uF(u,i),g=i?e(o.candidates):u;f===0?i?r.push.apply(r,g):r.push(u):n.push({documentOrder:a,tabIndex:f,item:o,isScope:i,content:g})}),n.sort(cF).reduce(function(o,a){return a.isScope?o.push.apply(o,a.content):o.push(a.content),o},[]).concatLine truncated
More details: https://www.material-tailwind.com/docs/react/collapse
`);var k=r.default.useRef(null),E=(0,g.useTheme)().navbar,j=E.styles,M=j.base.mobileNav;s=s??{},h=h??"";var D=(0,u.twMerge)((0,i.default)((0,f.default)(M)),h),L={unmount:{height:0,opacity:0,transition:{duration:.3,times:"[0.4, 0, 0.2, 1]"}},mount:{opacity:1,height:"".concat((N=k.current)===null||N===void 0?void 0:N.scrollHeight,"px"),transition:{duration:.3,times:"[0.4, 0, 0.2, 1]"}}},U=(0,a.default)(L,s),q=n.AnimatePresence,Q=(0,o.useMergeRefs)([l,k]);return r.default.createElement(n.LazyMotion,{features:n.domAnimation},r.default.createElement(q,null,r.default.createElement(n.m.div,_({},y,{ref:Q,className:D,initial:"unmount",exit:"unmount",animate:c?"mount":"unmount",variants:U}),v)))});w.displayName="MaterialTailwind.MobileNav",w.propTypes={open:b.propTypesOpen,animate:b.propTypesAnimate,className:b.propTypesClassName,children:b.propTypesChildren};var m=w})(RP);(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(l,c){for(var s in c)Object.defineProperty(l,s,{enumerable:!0,get:c[s]})}t(e,{Navbar:function(){return m},MobileNav:function(){return g.MobileNav},default:function(){return d}});var r=T(A),n=T(He),o=T(Ze),a=Ue,i=T(Lt),u=T(Ve),f=ze,g=RP,b=nf;function _(l,c,s){return c in l?Object.defineProperty(l,c,{value:s,enumerable:!0,configurable:!0,writable:!0}):l[c]=s,l}function S(){return S=Object.assign||function(l){for(var c=1;c<arguments.length;c++){var s=arguments[c];for(var h in s)Object.prototype.hasOwnProperty.call(s,h)&&(l[h]=s[h])}return l},S.apply(this,arguments)}function T(l){return l&&l.__esModule?l:{default:l}}function C(l,c){if(l==null)return{};var s=w(l,c),h,v;if(Object.getOwnPropertySymbols){var y=Object.getOwnPropertySymbols(l);for(v=0;v<y.length;v++)h=y[v],!(c.indexOf(h)>=0)&&Object.prototype.propertyIsEnumerable.call(l,h)&&(s[h]=l[h])}return s}function w(l,c){if(l==null)return{};var s={},h=Object.keys(l),v,y;for(y=0;y<h.length;y++)v=h[y],!(c.indexOf(v)>=0)&&(s[v]=l[v]);return s}var m=r.default.forwardRef(function(l,c){var s=l.variant,h=l.color,v=l.shadow,y=l.blurred,N=l.fullWidth,k=l.className,E=l.children,j=C(l,["variant","color","shadow","blurred","fullWidth","className","children"]),M=(0,f.useTheme)().navbar,D=M.defaultProps,L=M.valid,U=M.styles,q=U.base,Q=U.variants;s=s??D.variant,h=h??D.color,v=v??D.shadow,y=y??D.blurred,N=N??D.fullWidth,k=k??D.className;var W,z=(0,o.default)((0,u.default)(q.navbar.initial),(W={},_(W,(0,u.default)(q.navbar.shadow),v),_(W,(0,u.default)(q.navbar.blurred),y&&h==="white"),_(W,(0,u.default)(q.navbar.fullWidth),N),W)),H=(0,o.default)((0,u.default)(Q[(0,i.default)(L.variants,s,"filled")][(0,i.default)(L.colors,h,"white")])),K=(0,a.twMerge)((0,o.default)(z,H),k);return r.default.createElement("nav",S({},j,{ref:c,className:K}),E)});m.propTypes={variant:n.default.oneOf(b.propTypesVariant),color:n.default.oneOf(b.propTypesColor),shadow:b.propTypesShadow,blurred:b.propTypesBlurred,fullWidth:b.propTypesFullWidth,className:b.propTypesClassName,children:b.propTypesChildren},m.displayName="MaterialTailwind.Navbar";var d=Object.assign(m,{MobileNav:g.MobileNav})})(jP);var AP={},Sf={},wl={};(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(m,d){for(var l in d)Object.defineProperty(m,l,{enumerable:!0,get:d[l]})}t(e,{propTypesOpen:function(){return a},propTypesHandler:function(){return i},propTypesPlacement:function(){return u},propTypesOffset:function(){return f},propTypesDismiss:function(){return g},propTypesAnimate:function(){return b},propTypesContent:function(){return _},propTypesInteractive:function(){return S},propTypesClassName:function(){return T},propTypesChildren:function(){return C},propTypesContextValue:function(){return w}});var r=o(He),n=Rt;function o(m){return m&&m.__esModule?m:{default:m}}var a=r.default.bool,i=r.default.func,u=n.propTypesPlacements,f=n.propTypesOffsetType,g=n.propTypesDismissType,b=n.propTypesAnimation,_=r.default.node,S=r.default.bool,T=r.default.string,C=r.default.node.isRequired,w=r.default.shape({open:r.default.bool.isRequired,strategy:r.default.oneOf(["fixed","absolute"]).isRequired,x:r.default.number,y:r.default.number,context:r.default.instanceOf(Object).isRequired,reference:r.default.func.isRequired,floating:r.default.func.isRequired,getReferenceProps:r.default.func.isRequired,getFloatingProps:r.default.func.isRequired,appliedAnimation:b.isRequired,labelId:r.default.string.isRequired,descriptionId:r.default.string.isRequired}).isRequired})(wl);(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(f,g){for(var b in g)Object.defineProperty(f,b,{enumerable:!0,get:g[b]})}t(e,{PopoverContext:function(){return a},usePopover:function(){return i},PopoverContextProvider:function(){return u}});var r=o(A),n=wl;function o(f){return f&&f.__esModule?f:{default:f}}var a=r.default.createContext(null);a.displayName="MaterialTailwind.PopoverContext";function i(){var f=r.default.useContext(a);if(!f)throw new Error("usePopover() must be used within a Popover. It happens when you us Line truncated
${Se?"border-purple-500 ring-1 ring-purple-500/40":"border-transparent"}
${!ft||we?"opacity-75 bg-stormGrey-200/20":""}`,children:[O.jsxs("div",{className:"flex items-center space-x-4",id:"left",children:[O.jsx("input",{type:"checkbox",checked:Se,onChange:()=>ke(ee.Id,!ft),className:`h-5 w-5 text-purple-500 focus:ring-purple-400 border-gray-600 bg-gray-900 rounded ${ft?"":"opacity-40 cursor-not-allowed"}`,disabled:!ft}),O.jsx("div",{className:`w-12 h-12 flex items-center justify-center rounded-full
${ee.updateStatus==="✅"?"bg-mossGreen-600":ee.updateStatus==="⚠️"?"bg-warmAmberGlow-400":"bg-sunsetCoral-500"}`}),O.jsxs("div",{children:[O.jsx("p",{className:"text-lg antialiased font-sans font-semibold leading-normal text-inherit",children:ee.Name}),O.jsxs("p",{className:"text-sm antialiased font-sans font-light leading-normal text-inherit",children:["ID: ",ee.Id]}),ee.duplicateName&&O.jsx("p",{className:"text-xs text-amber-700",children:"⚠️ Doppelter Name erkannt"})]})]}),O.jsx("div",{className:"ml-auto flex flex-col items-end gap-1 text-sm",id:"right",children:Ie?O.jsxs(O.Fragment,{children:[O.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-warmAmberGlow-600",children:"Redeploy"}),O.jsx("span",{className:"text-warmAmberGlow-500",children:"läuft…"})]}):xe?O.jsxs(O.Fragment,{children:[O.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-warmAmberGlow-600",children:"Redeploy"}),O.jsx("span",{className:"text-warmAmberGlow-500",children:"in Warteliste…"})]}):dt?O.jsxs(O.Fragment,{children:[O.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-stormGrey-500",children:"System"}),O.jsx("span",{className:"text-gray-300",children:"Redeploy deaktiviert"})]}):Qe?O.jsxs(O.Fragment,{children:[O.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-stormGrey-500",children:"Status"}),O.jsx("span",{className:"text-mossGreen-600",children:"Aktuell"})]}):O.jsx(X.Button,{onClick:()=>Ke(ee.Id),disabled:we,className:"disabled:opacity-50 disabled:cursor-not-allowed",children:"Redeploy"})})]})},ee.Id)})}),O.jsx(Nf,{disabled:r||!!o})]})}const RB={idle:"Bereit",running:"Läuft",success:"Erfolgreich",error:"Fehlgeschlagen"},Xl={idle:"bg-stormGrey-700 text-gray-200",running:"bg-arcticBlue-600 text-white",success:"bg-mossGreen-600 text-white",error:"bg-sunsetCoral-600 text-white"},AB={initializing:"Vorbereitung","activating-maintenance":"Wartungsmodus aktivieren","executing-script":"Skript wird ausgeführt",waiting:"Warte auf Portainer",completed:"Abgeschlossen",failed:"Fehlgeschlagen"},MB={info:"text-arcticBlue-700",success:"text-mossGreen-700",warning:"text-warmAmberGlow-700",error:"text-sunsetCoral-700",stdout:"text-stormGrey-700",stderr:"text-warmAmberGlow-700",debug:"text-lavenderSmoke-700"},IB=e=>{if(!e)return"-";const t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleString("de-DE",{hour:"2-digit",minute:"2-digit",second:"2-digit"})},Ea=e=>{if(!e&&e!==0)return"-";const r=(n=>{if(n instanceof Date)return n;if(typeof n=="number"){const o=n>1e12?n:n*1e3;return new Date(o)}if(typeof n=="string"){const o=Number(n);if(!Number.isNaN(o)){const a=o>1e12?o:o*1e3;return new Date(a)}return new Date(n)}return null})(e);return!r||Number.isNaN(r.getTime())?String(e):r.toLocaleString("de-DE",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"})},Nx=e=>e===1?"Git":e===2?"Compose":e??"-",c0=()=>({host:"",port:"22",username:"",password:"",extraSshArgs:""});function DB(){var qn,Ta,El;const{showToast:e}=Cl(),{maintenance:t,update:r,script:n,ssh:o,loading:a,error:i,setMaintenanceMode:u,triggerUpdate:f,saveScript:g,resetScript:b,saveSshConfig:_,deleteSshConfig:S,testSshConnection:T,fetchSuperuserStatus:C,fetchSetupStatus:w,deleteSetupEndpoint:m,deleteSetupServer:d,updateSetupApiKey:l,removeSuperuserAccount:c}=Tl(),s=!!(t!=null&&t.active),h=t==null?void 0:t.message,v=(qn=t==null?void 0:t.extra)==null?void 0:qn.type,y=!!(r!=null&&r.running),[N,k]=A.useState(""),[E,j]=A.useState(!1),[M,D]=A.useState(!1),[L,U]=A.useState(""),[q,Q]=A.useState(!1),[W,z]=A.useState(()=>c0()),[H,K]=A.useState(!1),[Z,Y]=A.useState(!1),[re,ae]=A.useState(!1),[te,ve]=A.useState(!1),[de,ue]=A.useState(!1),[pe,fe]=A.useState(null);A.useEffect(()=>{if(!n)return;const ce=n.custom??n.default??n.effective??"";k(ce)},[n]),A.useEffect(()=>{if(!o){z(c0()),K(!1),Y(!1),fe(null);return}z({host:o.host??"",port:String(o.port??"22"),username:o.username??"",password:"",extraSshArgs:Array.isArray(o.extraSshArgs)?o.extraSshArgs.join(`
`):""}),K(!!o.passwordStored),Y(!1),fe(null)},[o]);const ke=A.useMemo(()=>n?n.source==="custom"&&typeof n.custom=="string"?n.custom:n.default??n.effective??"":"",[n]),Ee=n?N!==ke:!1,Oe=(n==null?void 0:n.source)==="custom"?"Benutzerdefiniert":"Standard",[_e,Me]=A.useState(!0),[Ke,nt]=A.useState(!1),[ye,Be]=A.useState(null),[Xe,je]=A.useState(""),[qe,$e]=A.useState(!1),[ee,ge]=A.useState(null),[xe,Ie]=A.useState(!0),[we,Se]=A.useState(""),[Qe,dt]=A.useState({}),[ft,Ut]=A.useState(null),[et,Gt]=A.useState(null),[Kt,At]=A.useState(null),[Xt,se]=A.useState([]),[be,Le]=A.useState(!0),[ot,lt]=A.useState(""),[yt,st]=A.useState(!1),[Mt,tr]=A.useState(null),[Po,Wr]=A.useState(null),xn=A.useRef(null),[vt,ii]=A.useState(null),[_n,Ur]=A.useState(!0),[va,ko]=A.useState(""),[Sl,Eo]=A.useState(!1),[No,Ol]=A.useState(null),dr=A.useRef(null),Hr=A.useCallback(async({silent:ce=!1}={})=>{var Pe,Ne;Me(!0),je("");try{const Re=await C(),Ye=!!(Re!=null&&Re.exists);nt(Ye),Be(Ye?Re.user??null:null)}catch(Re){const Ye=((Ne=(Pe=Re.response)==null?void 0:Pe.data)==null?void 0:Ne.error)||Re.message||"Superuser-Status konnte nicht geladen werden";nt(!1),Be(null),je(Ye),ce||e({variant:"error",title:"Statusabfrage fehlgeschlagen",description:Ye})}finally{Me(!1)}},[C,e]),jr=A.useCallback(async({silent:ce=!1}={})=>{var Pe,Ne;ce||Ie(!0),Se("");try{const Re=await w();ge(Re),dt({})}catch(Re){const Ye=((Ne=(Pe=Re.response)==null?void 0:Pe.data)==null?void 0:Ne.error)||Re.message||"Setup-Daten konnten nicht geladen werden";Se(Ye),ce||e({variant:"error",title:"Setup-Daten",description:Ye})}finally{Ie(!1)}},[w,e]),ao=A.useCallback(async({silent:ce=!1}={})=>{if(dr.current)return dr.current;const Pe=(async()=>{var Ne,Re;ce?Eo(!0):Ur(!0),ko("");try{const Tt=(await ct.get("/api/maintenance/portainer-status")).data??{};ii(Tt),Ol(new Date)}catch(Ye){const Tt=((Re=(Ne=Ye.response)==null?void 0:Ne.data)==null?void 0:Re.error)||Ye.message||"Fehler beim Prüfen des Portainer-Status";ko(Tt),e({variant:"error",title:"Statusaktualisierung fehlgeschlagen",description:Tt})}finally{ce?Eo(!1):Ur(!1),dr.current=null}})();return dr.current=Pe,Pe},[e]),Un=A.useCallback(async({silent:ce=!1}={})=>{if(s||y){se([]),lt("Wartungsmodus aktiv – Duplikat-Verwaltung ist vorübergehend deaktiviert."),Le(!1),st(!1);return}if(xn.current)return xn.current;const Pe=(async()=>{var Ne,Re,Ye;ce?st(!0):Le(!0),lt("");try{const xt=(await ct.get("/api/maintenance/duplicates")).data,$t=Array.isArray(xt)?xt:(xt==null?void 0:xt.items)??[];se($t),Wr(new Date)}catch(Tt){if(((Ne=Tt.response)==null?void 0:Ne.status)===423){const xt="Wartungsmodus aktiv – Duplikat-Verwaltung ist vorübergehend deaktiviert.";se([]),lt(xt),e({variant:"warning",title:"Duplikat-Verwaltung gesperrt",description:xt})}else{const xt=((Ye=(Re=Tt.response)==null?void 0:Re.data)==null?void 0:Ye.error)||Tt.message||"Fehler beim Laden der Wartungsdaten";lt(xt),e({variant:"error",title:"Duplikate konnten nicht geladen werden",description:xt})}}finally{ce?st(!1):Le(!1),xn.current=null}})();return xn.current=Pe,Pe},[s,y,e]);A.useEffect(()=>{Hr({silent:!0})},[Hr]),A.useEffect(()=>{jr()},[jr]),A.useEffect(()=>{ao()},[ao]),A.useEffect(()=>{Un()},[Un]);const Cn=A.useMemo(()=>{var ce;return((ce=ee==null?void 0:ee.servers)==null?void 0:ce.items)??[]},[ee]),$r=A.useMemo(()=>{var ce;return((ce=ee==null?void 0:ee.endpoints)==null?void 0:ce.items)??[]},[ee]),ya=A.useMemo(()=>{var Pe;const ce=((Pe=ee==null?void 0:ee.apiKeys)==null?void 0:Pe.items)??[];return new Map(ce.map(Ne=>[Ne.serverId,Ne]))},[ee]),wa=A.useMemo(()=>!!(ee!=null&&ee.setupComplete),[ee]),Hn=A.useMemo(()=>{const ce=Array.isArray(Xt)?Xt.length:0,Pe=Array.isArray(Xt)?Xt.reduce((Ne,Re)=>{var Ye;return Ne+(((Ye=Re==null?void 0:Re.duplicates)==null?void 0:Ye.length)||0)},0):0;return{groups:ce,duplicateCount:Pe}},[Xt]),qr=A.useCallback((ce,Pe)=>{z(Ne=>({...Ne,[ce]:Pe})),ce==="password"&&K(!1)},[]),$n=A.useMemo(()=>{const ce={host:W.host.trim(),port:Number.parseInt(W.port,10)||22,username:W.username.trim(),extraSshArgs:(W.extraSshArgs||"").split(/\r?\n/).map(Ne=>Ne.trim()).filter(Boolean)},Pe=W.password??"";return Pe?ce.password=Pe:H||(ce.password=""),ce},[W,H]),li=A.useCallback(async()=>{var ce,Pe;if(n)try{j(!0),await g(N),e({variant:"success",title:"Skript gespeichert",description:"Das benutzerdefinierte Portainer-Update-Skript wurde aktualisiert."})}catch(Ne){const Re=((Pe=(ce=Ne.response)==null?void 0:ce.data)==null?void 0:Pe.error)||Ne.message||"Skript konnte nicht gespeichert werden";e({variant:"error",title:"Speichern fehlgeschlagen",description:Re})}finally{j(!1)}},[g,N,n,e]),io=A.useCallback(async()=>{var ce,Pe;try{j(!0),await b(),e({variant:"info",title:"Standardskript wiederhergestellt",description:"Es wird wieder das Standard-Update-Skript verwendet."})}catch(Ne){const Re=((Pe=(ce=Ne.response)==null?void 0:ce.data)==null?void 0:Pe.error)||Ne.message||"Standardskript konnte nicht wiederhergestellt werden";e({variant:"error",title:"Zurücksetzen fehlgeschlagen",descr Line truncated
Dies setzt die Superuser-Einrichtung zurück.`)))try{$e(!0);const Re=await c(),Ye=Number((Re==null?void 0:Re.usersRemoved)??0);e({variant:"success",title:"Superuser gelöscht",description:Ye>1?`${Ye} Konten aus der Superuser-Gruppe wurden entfernt.`:"Superuser-Konto wurde entfernt."}),await Hr({silent:!0})}catch(Re){if(((ce=Re.response)==null?void 0:ce.status)===404)await Hr({silent:!0}),e({variant:"info",title:"Kein Superuser vorhanden",description:"Es ist kein Superuser mehr vorhanden."});else{const Tt=((Ne=(Pe=Re.response)==null?void 0:Pe.data)==null?void 0:Ne.error)||Re.message||"Superuser konnte nicht gelöscht werden";e({variant:"error",title:"Löschen fehlgeschlagen",description:Tt})}}finally{$e(!1)}},[qe,_e,Ke,c,Hr,e]),kl=A.useCallback(async ce=>{var Ye,Tt;if(Kt===ce)return;const Pe=$r.find(xt=>xt.id===ce);if(!Pe)return;const Ne=Pe.name||`Endpoint ${Pe.external_id}`,Re=`Endpoint "${Ne}" wirklich löschen?${Pe.is_default?`
Dieser Endpoint ist aktuell als Standard markiert.`:""}`;if(!(typeof window<"u"&&!window.confirm(Re))){At(ce);try{await m(ce),e({variant:"success",title:"Endpoint gelöscht",description:`Endpoint "${Ne}" wurde entfernt.`}),await jr({silent:!0})}catch(xt){const $t=((Tt=(Ye=xt.response)==null?void 0:Ye.data)==null?void 0:Tt.error)||xt.message||"Endpoint konnte nicht gelöscht werden.";e({variant:"error",title:"Endpoint löschen fehlgeschlagen",description:$t})}finally{At(null)}}},[m,Kt,jr,$r,e]),_a=A.useCallback(async ce=>{var Tt,xt;if(et===ce)return;const Pe=Cn.find($t=>$t.id===ce);if(!Pe)return;const Ne=$r.filter($t=>$t.server_id===ce),Re=Pe.name||Pe.url||`Server ${ce}`,Ye=[`Server "${Re}" wirklich löschen?`,Ne.length?`Dabei werden ${Ne.length} zugeordnete Endpoint(s) entfernt.`:null,"Das System benötigt anschließend erneut einen gültigen Server/Endpoint im Setup."].filter(Boolean).join(`
`);if(!(typeof window<"u"&&!window.confirm(Ye))){Gt(ce);try{await d(ce),e({variant:"success",title:"Server gelöscht",description:`Server "${Re}" wurde entfernt.`}),await jr({silent:!0})}catch($t){const mr=((xt=(Tt=$t.response)==null?void 0:Tt.data)==null?void 0:xt.error)||$t.message||"Server konnte nicht gelöscht werden.";e({variant:"error",title:"Server löschen fehlgeschlagen",description:mr})}finally{Gt(null)}}},[d,jr,et,$r,Cn,e]),lo=A.useCallback((ce,Pe)=>{dt(Ne=>({...Ne,[ce]:Pe}))},[]),Ot=A.useCallback(async ce=>{var Ne,Re;const Pe=(Qe[ce]??"").trim();if(!Pe){e({variant:"warning",title:"API-Key fehlt",description:"Bitte gib einen API-Key ein."});return}if(ft!==ce){Ut(ce);try{await l(ce,Pe),e({variant:"success",title:"API-Key aktualisiert",description:"Der API-Key wurde gespeichert."}),dt(Ye=>({...Ye,[ce]:""})),await jr({silent:!0})}catch(Ye){const Tt=((Re=(Ne=Ye.response)==null?void 0:Ne.data)==null?void 0:Re.error)||Ye.message||"API-Key konnte nicht aktualisiert werden.";e({variant:"error",title:"Aktualisierung fehlgeschlagen",description:Tt})}finally{Ut(null)}}},[Qe,ft,jr,e,l]),Ht=A.useCallback(async ce=>{var Pe,Ne;if(!(a||q)&&ce!==s){if(y){e({variant:"warning",title:"Aktion nicht möglich",description:"Während eines laufenden Updates kann der Wartungsmodus nicht geändert werden."});return}Q(!0);try{const Re=ce&&!!v,Ye={active:ce};Re&&h?Ye.message=h:Ye.message=null,await u(Ye),e({variant:ce?"info":"success",title:ce?"Wartungsmodus aktiviert":"Wartungsmodus deaktiviert",description:ce?"Benutzer sehen jetzt die Wartungsseite.":"StackPulse steht wieder zur Verfügung."})}catch(Re){const Ye=((Ne=(Pe=Re.response)==null?void 0:Pe.data)==null?void 0:Ne.error)||Re.message||"Wartungsmodus konnte nicht geändert werden";e({variant:"error",title:"Änderung fehlgeschlagen",description:Ye})}finally{Q(!1)}}},[a,q,s,y,u,h,v,e]),kt=A.useCallback(async()=>{var Pe,Ne;if(s||y){e({variant:"warning",title:"Update nicht möglich",description:"Während eines aktiven Wartungsmodus kann kein weiteres Update gestartet werden."});return}const ce=(vt==null?void 0:vt.latestVersion)??(vt==null?void 0:vt.currentVersion)??"unbekannt";if(!(typeof window<"u"&&!window.confirm(`Portainer-Update starten?
Zielversion: ${ce}.
Während des Updates befindet sich StackPulse im Wartungsmodus.
Fortfahren?`)))try{U(""),D(!0),await f(),e({variant:"info",title:"Update gestartet",description:"Das Portainer-Update wurde gestartet."})}catch(Re){const Ye=((Ne=(Pe=Re.response)==null?void 0:Pe.data)==null?void 0:Ne.error)||Re.message||"Portainer-Update konnte nicht gestartet werden";U(Ye),e({variant:"error",title:"Update fehlgeschlagen",description:Ye})}finally{D(!1)}},[s,y,vt,f,e]),on=A.useCallback(async ce=>{var Ye,Tt,xt,$t;if(!ce)return;if(s||y){e({variant:"warning",title:"Aktion nicht möglich",description:"Während des Wartungsmodus sind Bereinigungen deaktiviert."});return}const Pe=(Ye=ce.canonical)==null?void 0:Ye.Id;if(!Pe)return;const Ne=(ce.duplicates||[]).map(mr=>mr.Id).filter(Boolean);if(!Ne.length)return;const Re=((Tt=ce.canonical)==null?void 0:Tt.Name)||ce.name||`Stack ${Pe}`;if(!(typeof window<"u"&&!window.confirm(`Bereinigung für "${Re}" starten?
Es werden ${Ne.length} Duplikate entfernt: ${Ne.join(", ")}`))){tr(String(Pe));try{const so=(await ct.post("/api/maintenance/duplicates/cleanup",{canonicalId:Pe,duplicateIds:Ne})).data??{};if(so.success===!1)throw new Error(so.error||"Bereinigung fehlgeschlagen");const Nl=Array.isArray(so.results)?so.results.filter(Sa=>Sa.status==="deleted").map(Sa=>Sa.id):Ne;e({variant:"success",title:"Bereinigung abgeschlossen",description:`${Re} – entfernte IDs: ${Nl.join(", ")}`}),await Un({silent:!0})}catch(mr){const so=(($t=(xt=mr.response)==null?void 0:xt.data)==null?void 0:$t.error)||mr.message||"Bereinigung fehlgeschlagen";e({variant:"error",title:"Bereinigung fehlgeschlagen",description:so})}finally{tr(null)}}},[Un,s,e,y]);t!=null&&t.activatedAt&&Ea(t.activatedAt),t!=null&&t.updatedAt&&Ea(t.updatedAt);const Tr=s||y,an=(r==null?void 0:r.status)??(y?"running":"idle"),Ca=RB[an]??an,si=Xl[an]??Xl.idle,ui=r!=null&&r.stage?AB[r.stage]??r.stage:"–",ci=(r==null?void 0:r.logs)??[],Rf=(r==null?void 0:r.targetVersion)??"-",Af=(r==null?void 0:r.resultVersion)??"-",Mf=r!=null&&r.startedAt?Ea(r.startedAt):"-",If=r!=null&&r.finishedAt?Ea(r.finishedAt):"-",mu=(r==null?void 0:r.message)??(y?"Update läuft…":""),bu=y||E||a;return O.jsxs("div",{className:"mt-12 mb-8 flex flex-col gap-12",children:[(s||y)&&O.jsx("div",{className:"rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100",children:O.jsxs("div",{className:"flex flex-col gap-1",children:[O.jsxs("span",{children:["Wartungsmodus aktiv",h?` – ${h}`:y?" – Portainer-Update läuft":"","."]}),y&&O.jsxs("span",{className:"text-xs text-indigo-900",children:["Phase: ",ui]})]})}),O.jsxs(X.Card,{children:[O.jsx(X.CardHeader,{variant:"gradient",color:"gray",className:"mb-5 p-4",children:O.jsx(X.Typography,{variant:"h6",color:"white",className:"flex items-center justify-between",children:O.jsx("span",{children:"Wartungsmodus"})})}),O.jsxs(X.CardBody,{className:"flex flex-col gap-4 p-4",children:[O.jsxs("div",{className:"flex items-start justify-between gap-4",children:[O.jsx("div",{className:"space-y-1",children:O.jsx("p",{className:"text-sm antialiased font-sans text-sm font-light leading-normal text-inherit",children:"Schaltet StackPulse für Benutzer in den Wartungsmodus."})}),O.jsx(X.Switch,{checked:s,disabled:q||a||y,onChange:ce=>Ht(ce.target.checked),ripple:!1,color:"amber"})]}),q&&O.jsx("p",{className:"text-xs text-stormGrey-500",children:"Wartungsmodus wird aktualisiert…"})]})]}),O.jsxs(X.Card,{children:[O.jsx(X.CardHeader,{variant:"gradient",color:"gray",className:"mb-5 p-4",children:O.jsx(X.Typography,{variant:"h6",color:"white",className:"flex items-center justify-between",children:O.jsx("span",{children:"Server & Endpoints"})})}),O.jsxs(X.CardBody,{className:"flex flex-col gap-4 p-4",children:[xe&&O.jsx("p",{className:"text-sm text-stormGrey-500",children:"Daten werden geladen…"}),!xe&&we&&O.jsx(Alert,{color:"red",className:"border border-red-200 bg-red-50 text-red-700",children:we}),!xe&&!we&&O.jsxs("div",{className:"flex flex-col gap-6",children:[O.jsxs("div",{className:"flex flex-col gap-3",children:[O.jsxs("div",{className:"flex items-center justify-between",children:[O.jsx(X.Typography,{variant:"small",color:"blue-gray",className:"font-semibold uppercase",children:"Server"}),O.jsxs(X.Typography,{variant:"small",color:"blue-gray",children:[Cn.length," vorhanden"]})]}),Cn.length===0?O.jsx("p",{className:"text-sm text-stormGrey-500",children:"Es sind derzeit keine Server hinterlegt."}):O.jsx("div",{className:"flex flex-col gap-3",children:Cn.map(ce=>{const Pe=ya.get(ce.id)||null,Ne=!!(Pe!=null&&Pe.hasKey),Re=Pe!=null&&Pe.updatedAt?new Date(Pe.updatedAt).toLocaleString("de-DE",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}):null;return O.jsxs("div",{className:"flex flex-col gap-3 rounded-md border border-blue-gray-100 p-3 md:flex-row md:items-start md:justify-between",children:[O.jsxs("div",{className:"flex flex-col gap-1",children:[O.jsx("p",{className:"text-sm font-medium text-stormGrey-900",children:ce.name||"Ohne Namen"}),O.jsx("p",{className:"text-xs text-stormGrey-500 break-all",children:ce.url}),O.jsxs("p",{className:"text-xs text-stormGrey-500",children:["API-Key: ",Ne?"vorhanden":"nicht hinterlegt",Re?` (aktualisiert am ${Re})`:""]})]}),O.jsxs("div",{className:"flex w-full flex-col gap-2 md:max-w-xs",children:[O.jsx(X.Input,{type:"password",label:"Neuer API-Key",value:Qe[ce.id]??"",onChange:Ye=>lo(ce.id,Ye.target.value),disabled:xe||ft===ce.id}),O.jsxs("div",{className:"flex items-center gap-2 justify-end",children:[O.jsx(X.Button,{variant:"text",color:"red",size:"sm",onClick:()=>_a(ce.id),disabled:xe||et===ce.id||Kt!==null||ft===ce.id,children:et===ce.id?"Lösche…":"Löschen"}),O.jsx(X.Button,{color:"blue",size:"sm",onClick:()=>Ot(ce.id),disabled:xe||ft===ce.id,children:ft===ce.id?"Speichere…":"API-Key speichern"})]})]})]},ce.id)})})]}),O.jsxs("div",{className:"flex flex-col gap-3" Line truncated
+93
View File
@@ -0,0 +1,93 @@
function Aj(e,t){for(var r=0;r<t.length;r++){const n=t[r];if(typeof n!="string"&&!Array.isArray(n)){for(const o in n)if(o!=="default"&&!(o in e)){const a=Object.getOwnPropertyDescriptor(n,o);a&&Object.defineProperty(e,o,a.get?a:{enumerable:!0,get:()=>n[o]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))n(o);new MutationObserver(o=>{for(const a of o)if(a.type==="childList")for(const i of a.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&n(i)}).observe(document,{childList:!0,subtree:!0});function r(o){const a={};return o.integrity&&(a.integrity=o.integrity),o.referrerPolicy&&(a.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?a.credentials="include":o.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function n(o){if(o.ep)return;o.ep=!0;const a=r(o);fetch(o.href,a)}})();function Ax(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function Qs(e){if(e.__esModule)return e;var t=e.default;if(typeof t=="function"){var r=function n(){return this instanceof n?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};r.prototype=t.prototype}else r={};return Object.defineProperty(r,"__esModule",{value:!0}),Object.keys(e).forEach(function(n){var o=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(r,n,o.get?o:{enumerable:!0,get:function(){return e[n]}})}),r}var Mx={exports:{}},Ad={},Ix={exports:{}},dt={};/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var Xs=Symbol.for("react.element"),Mj=Symbol.for("react.portal"),Ij=Symbol.for("react.fragment"),Dj=Symbol.for("react.strict_mode"),Lj=Symbol.for("react.profiler"),Fj=Symbol.for("react.provider"),zj=Symbol.for("react.context"),Bj=Symbol.for("react.forward_ref"),Vj=Symbol.for("react.suspense"),Uj=Symbol.for("react.memo"),Wj=Symbol.for("react.lazy"),Uv=Symbol.iterator;function Hj(e){return e===null||typeof e!="object"?null:(e=Uv&&e[Uv]||e["@@iterator"],typeof e=="function"?e:null)}var Dx={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Lx=Object.assign,Fx={};function fl(e,t,r){this.props=e,this.context=t,this.refs=Fx,this.updater=r||Dx}fl.prototype.isReactComponent={};fl.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};fl.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function zx(){}zx.prototype=fl.prototype;function $h(e,t,r){this.props=e,this.context=t,this.refs=Fx,this.updater=r||Dx}var qh=$h.prototype=new zx;qh.constructor=$h;Lx(qh,fl.prototype);qh.isPureReactComponent=!0;var Wv=Array.isArray,Bx=Object.prototype.hasOwnProperty,Gh={current:null},Vx={key:!0,ref:!0,__self:!0,__source:!0};function Ux(e,t,r){var n,o={},a=null,i=null;if(t!=null)for(n in t.ref!==void 0&&(i=t.ref),t.key!==void 0&&(a=""+t.key),t)Bx.call(t,n)&&!Vx.hasOwnProperty(n)&&(o[n]=t[n]);var u=arguments.length-2;if(u===1)o.children=r;else if(1<u){for(var f=Array(u),g=0;g<u;g++)f[g]=arguments[g+2];o.children=f}if(e&&e.defaultProps)for(n in u=e.defaultProps,u)o[n]===void 0&&(o[n]=u[n]);return{$$typeof:Xs,type:e,key:a,ref:i,props:o,_owner:Gh.current}}function $j(e,t){return{$$typeof:Xs,type:e.type,key:t,ref:e.ref,props:e.props,_owner:e._owner}}function Kh(e){return typeof e=="object"&&e!==null&&e.$$typeof===Xs}function qj(e){var t={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,function(r){return t[r]})}var Hv=/\/+/g;function pp(e,t){return typeof e=="object"&&e!==null&&e.key!=null?qj(""+e.key):t.toString(36)}function ic(e,t,r,n,o){var a=typeof e;(a==="undefined"||a==="boolean")&&(e=null);var i=!1;if(e===null)i=!0;else switch(a){case"string":case"number":i=!0;break;case"object":switch(e.$$typeof){case Xs:case Mj:i=!0}}if(i)return i=e,o=o(i),e=n===""?"."+pp(i,0):n,Wv(o)?(r="",e!=null&&(r=e.replace(Hv,"$&/")+"/"),ic(o,t,r,"",function(g){return g})):o!=null&&(Kh(o)&&(o=$j(o,r+(!o.key||i&&i.key===o.key?"":(""+o.key).replace(Hv,"$&/")+"/")+e)),t.push(o)),1;if(i=0,n=n===""?".":n+":",Wv(e))for(var u=0;u<e.length;u++){a=e[u];var f=n+pp(a,u);i+=ic(a,t,r,f,o)}else if(f=Hj(e),typeof f=="function")for(e=f.call(e),u=0;!(a=e.next()).done;)a=a.value,f=n+pp(a,u++),i+=ic(a,t,r,f,o);else if(a==="object")throw t=String(e),Error("Objects are not valid as a React child (found: "+(t==="[object Object]"?"object with keys {"+Object.keys(e).join(", ")+"}":t)+"). If you meant to render a collection of children, use an array instead.");return i}function Ru(e,t,r){if(e==null)return e;var n=[],o=0;return ic(e,n,"","",function(a){return t.call(r,a,o++)}),n}function Gj(e){if(e._status===-1){var t=e._result;t=t(),t.then(function(r){(e._status===0||e._status===-1)&&(e._status=1,e._result=r)},function(r){(e._status===0||e._status===-1)&&(e._status=2,e._result=r)}),e._status===-1&&(e._status=0,e._result=t)}if(e._status===1)return e._result.default;throw e._result}var Dr={current:null},lc={transition:null},Kj={ReactCurrentDispatcher:Dr,ReactCurrentBatchConfig:lc,ReactCurrentOwner:Gh};dt.Children={map:Ru,forEach:function(e,t,r){Ru(e,function(){t.apply(this,arguments)},r)},count:function(e){var t=0;return Ru(e,function(){t++}),t},toArray:function(e){return Ru(e,function(t){return t})||[]},only:function(e){if(!Kh(e))throw Error("React.Children.only expected to receive a single React element child.");return e}};dt.Component=fl;dt.Fragment=Ij;dt.Profiler=Lj;dt.PureComponent=$h;dt.StrictMode=Dj;dt.Suspense=Vj;dt.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=Kj;dt.cloneElement=function(e,t,r){if(e==null)throw Error("React.cloneElement(...): The argument must be a React element, but you passed "+e+".");var n=Lx({},e.props),o=e.key,a=e.ref,i=e._owner;if(t!=null){if(t.ref!==void 0&&(a=t.ref,i=Gh.current),t.key!==void 0&&(o=""+t.key),e.type&&e.type.defaultProps)var u=e.type.defaultProps;for(f in t)Bx.call(t,f)&&!Vx.hasOwnProperty(f)&&(n[f]=t[f]===void 0&&u!==void 0?u[f]:t[f])}var f=arguments.length-2;if(f===1)n.children=r;else if(1<f){u=Array(f);for(var g=0;g<f;g++)u[g]=arguments[g+2];n.children=u}return{$$typeof:Xs,type:e.type,key:o,ref:a,props:n,_owner:i}};dt.createContext=function(e){return e={$$typeof:zj,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null,_defaultValue:null,_globalName:null},e.Provider={$$typeof:Fj,_conLine truncated
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var Yj=j,Qj=Symbol.for("react.element"),Xj=Symbol.for("react.fragment"),Jj=Object.prototype.hasOwnProperty,Zj=Yj.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,e2={key:!0,ref:!0,__self:!0,__source:!0};function Wx(e,t,r){var n,o={},a=null,i=null;r!==void 0&&(a=""+r),t.key!==void 0&&(a=""+t.key),t.ref!==void 0&&(i=t.ref);for(n in t)Jj.call(t,n)&&!e2.hasOwnProperty(n)&&(o[n]=t[n]);if(e&&e.defaultProps)for(n in t=e.defaultProps,t)o[n]===void 0&&(o[n]=t[n]);return{$$typeof:Qj,type:e,key:a,ref:i,props:o,_owner:Zj.current}}Ad.Fragment=Xj;Ad.jsx=Wx;Ad.jsxs=Wx;Mx.exports=Ad;var T=Mx.exports;function Hx(e,t){return function(){return e.apply(t,arguments)}}const{toString:t2}=Object.prototype,{getPrototypeOf:Qh}=Object,Md=(e=>t=>{const r=t2.call(t);return e[r]||(e[r]=r.slice(8,-1).toLowerCase())})(Object.create(null)),qn=e=>(e=e.toLowerCase(),t=>Md(t)===e),Id=e=>t=>typeof t===e,{isArray:pl}=Array,_s=Id("undefined");function r2(e){return e!==null&&!_s(e)&&e.constructor!==null&&!_s(e.constructor)&&tn(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}const $x=qn("ArrayBuffer");function n2(e){let t;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?t=ArrayBuffer.isView(e):t=e&&e.buffer&&$x(e.buffer),t}const o2=Id("string"),tn=Id("function"),qx=Id("number"),Dd=e=>e!==null&&typeof e=="object",a2=e=>e===!0||e===!1,sc=e=>{if(Md(e)!=="object")return!1;const t=Qh(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(Symbol.toStringTag in e)&&!(Symbol.iterator in e)},i2=qn("Date"),l2=qn("File"),s2=qn("Blob"),u2=qn("FileList"),c2=e=>Dd(e)&&tn(e.pipe),d2=e=>{let t;return e&&(typeof FormData=="function"&&e instanceof FormData||tn(e.append)&&((t=Md(e))==="formdata"||t==="object"&&tn(e.toString)&&e.toString()==="[object FormData]"))},f2=qn("URLSearchParams"),[p2,h2,g2,m2]=["ReadableStream","Request","Response","Headers"].map(qn),b2=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function Js(e,t,{allOwnKeys:r=!1}={}){if(e===null||typeof e>"u")return;let n,o;if(typeof e!="object"&&(e=[e]),pl(e))for(n=0,o=e.length;n<o;n++)t.call(null,e[n],n,e);else{const a=r?Object.getOwnPropertyNames(e):Object.keys(e),i=a.length;let u;for(n=0;n<i;n++)u=a[n],t.call(null,e[u],u,e)}}function Gx(e,t){t=t.toLowerCase();const r=Object.keys(e);let n=r.length,o;for(;n-- >0;)if(o=r[n],t===o.toLowerCase())return o;return null}const za=(()=>typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:global)(),Kx=e=>!_s(e)&&e!==za;function d0(){const{caseless:e}=Kx(this)&&this||{},t={},r=(n,o)=>{const a=e&&Gx(t,o)||o;sc(t[a])&&sc(n)?t[a]=d0(t[a],n):sc(n)?t[a]=d0({},n):pl(n)?t[a]=n.slice():t[a]=n};for(let n=0,o=arguments.length;n<o;n++)arguments[n]&&Js(arguments[n],r);return t}const v2=(e,t,r,{allOwnKeys:n}={})=>(Js(t,(o,a)=>{r&&tn(o)?e[a]=Hx(o,r):e[a]=o},{allOwnKeys:n}),e),y2=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),w2=(e,t,r,n)=>{e.prototype=Object.create(t.prototype,n),e.prototype.constructor=e,Object.defineProperty(e,"super",{value:t.prototype}),r&&Object.assign(e.prototype,r)},x2=(e,t,r,n)=>{let o,a,i;const u={};if(t=t||{},e==null)return t;do{for(o=Object.getOwnPropertyNames(e),a=o.length;a-- >0;)i=o[a],(!n||n(i,e,t))&&!u[i]&&(t[i]=e[i],u[i]=!0);e=r!==!1&&Qh(e)}while(e&&(!r||r(e,t))&&e!==Object.prototype);return t},_2=(e,t,r)=>{e=String(e),(r===void 0||r>e.length)&&(r=e.length),r-=t.length;const n=e.indexOf(t,r);return n!==-1&&n===r},C2=e=>{if(!e)return null;if(pl(e))return e;let t=e.length;if(!qx(t))return null;const r=new Array(t);for(;t-- >0;)r[t]=e[t];return r},S2=(e=>t=>e&&t instanceof e)(typeof Uint8Array<"u"&&Qh(Uint8Array)),T2=(e,t)=>{const n=(e&&e[Symbol.iterator]).call(e);let o;for(;(o=n.next())&&!o.done;){const a=o.value;t.call(e,a[0],a[1])}},O2=(e,t)=>{let r;const n=[];for(;(r=e.exec(t))!==null;)n.push(r);return n},P2=qn("HTMLFormElement"),k2=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(r,n,o){return n.toUpperCase()+o}),$v=(({hasOwnProperty:e})=>(t,r)=>e.call(t,r))(Object.prototype),E2=qn("RegExp"),Yx=(e,t)=>{const r=Object.getOwnPropertyDescriptors(e),n={};Js(r,(o,a)=>{let i;(i=t(o,a,e))!==!1&&(n[a]=i||o)}),Object.defineProperties(e,n)},N2=e=>{Yx(e,(t,r)=>{if(tn(e)&&["arguments","caller","callee"].indexOf(r)!==-1)return!1;const n=e[r];if(tn(n)){if(t.enumerable=!1,"writable"in t){t.writable=!1;return}t.set||(t.set=()=>{throw Error("Can not rewrite read-only method '"+r+"'")})}})},j2=(e,t)=>{const r={},n=o=>{o.forEach(a=>{r[a]=!0})};return pl(e)?n(e):n(String(e).split(t)),r},R2=()=>{},A2=(e,t)=>e!=null&&Number.isFinite(e=+e)?e:t,hp="abcdefghijklmnopqrstuvwxyz",qv="0123456789",Qx={DIGIT:qv,ALPHA:hp,ALPHA_DIGIT:hp+hp.toUpperCase()+qv},M2=(e=16,t=Qx.ALPHA_DIGIT)=>{let r="";const{length:n}=t;for(;e--;)r+=t[Math.random()*n|0];return r};function I2(e){return!!(e&&tn(e.append)&&e[Symbol.toStringTag]==="FormData"&&e[Symbol.iterator])}const D2=e=>{const t=new Array(10),r=(n,o)=>{if(Dd(n)){if(t.indexOf(n)>=0)return;if(!("toJSON"in n)){t[o]=n;const a=pLine truncated
`).forEach(function(i){o=i.indexOf(":"),r=i.substring(0,o).trim().toLowerCase(),n=i.substring(o+1).trim(),!(!r||t[r]&&nR[r])&&(r==="set-cookie"?t[r]?t[r].push(n):t[r]=[n]:t[r]=t[r]?t[r]+", "+n:n)}),t},Qv=Symbol("internals");function Ul(e){return e&&String(e).trim().toLowerCase()}function uc(e){return e===!1||e==null?e:ye.isArray(e)?e.map(uc):String(e)}function aR(e){const t=Object.create(null),r=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let n;for(;n=r.exec(e);)t[n[1]]=n[2];return t}const iR=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function gp(e,t,r,n,o){if(ye.isFunction(n))return n.call(this,t,r);if(o&&(t=r),!!ye.isString(t)){if(ye.isString(n))return t.indexOf(n)!==-1;if(ye.isRegExp(n))return n.test(t)}}function lR(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(t,r,n)=>r.toUpperCase()+n)}function sR(e,t){const r=ye.toCamelCase(" "+t);["get","set","has"].forEach(n=>{Object.defineProperty(e,n+r,{value:function(o,a,i){return this[n].call(this,t,o,a,i)},configurable:!0})})}class Fd{constructor(t){t&&this.set(t)}set(t,r,n){const o=this;function a(u,f,g){const b=Ul(f);if(!b)throw new Error("header name must be a non-empty string");const _=ye.findKey(o,b);(!_||o[_]===void 0||g===!0||g===void 0&&o[_]!==!1)&&(o[_||f]=uc(u))}const i=(u,f)=>ye.forEach(u,(g,b)=>a(g,b,f));if(ye.isPlainObject(t)||t instanceof this.constructor)i(t,r);else if(ye.isString(t)&&(t=t.trim())&&!iR(t))i(oR(t),r);else if(ye.isHeaders(t))for(const[u,f]of t.entries())a(f,u,n);else t!=null&&a(r,t,n);return this}get(t,r){if(t=Ul(t),t){const n=ye.findKey(this,t);if(n){const o=this[n];if(!r)return o;if(r===!0)return aR(o);if(ye.isFunction(r))return r.call(this,o,n);if(ye.isRegExp(r))return r.exec(o);throw new TypeError("parser must be boolean|regexp|function")}}}has(t,r){if(t=Ul(t),t){const n=ye.findKey(this,t);return!!(n&&this[n]!==void 0&&(!r||gp(this,this[n],n,r)))}return!1}delete(t,r){const n=this;let o=!1;function a(i){if(i=Ul(i),i){const u=ye.findKey(n,i);u&&(!r||gp(n,n[u],u,r))&&(delete n[u],o=!0)}}return ye.isArray(t)?t.forEach(a):a(t),o}clear(t){const r=Object.keys(this);let n=r.length,o=!1;for(;n--;){const a=r[n];(!t||gp(this,this[a],a,t,!0))&&(delete this[a],o=!0)}return o}normalize(t){const r=this,n={};return ye.forEach(this,(o,a)=>{const i=ye.findKey(n,a);if(i){r[i]=uc(o),delete r[a];return}const u=t?lR(a):String(a).trim();u!==a&&delete r[a],r[u]=uc(o),n[u]=!0}),this}concat(...t){return this.constructor.concat(this,...t)}toJSON(t){const r=Object.create(null);return ye.forEach(this,(n,o)=>{n!=null&&n!==!1&&(r[o]=t&&ye.isArray(n)?n.join(", "):n)}),r}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([t,r])=>t+": "+r).join(`
`)}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(t){return t instanceof this?t:new this(t)}static concat(t,...r){const n=new this(t);return r.forEach(o=>n.set(o)),n}static accessor(t){const n=(this[Qv]=this[Qv]={accessors:{}}).accessors,o=this.prototype;function a(i){const u=Ul(i);n[u]||(sR(o,i),n[u]=!0)}return ye.isArray(t)?t.forEach(a):a(t),this}}Fd.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);ye.reduceDescriptors(Fd.prototype,({value:e},t)=>{let r=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(n){this[r]=n}}});ye.freezeMethods(Fd);const Ln=Fd;function mp(e,t){const r=this||eg,n=t||r,o=Ln.from(n.headers);let a=n.data;return ye.forEach(e,function(u){a=u.call(r,a,o.normalize(),t?t.status:void 0)}),o.normalize(),a}function a_(e){return!!(e&&e.__CANCEL__)}function hl(e,t,r){it.call(this,e??"canceled",it.ERR_CANCELED,t,r),this.name="CanceledError"}ye.inherits(hl,it,{__CANCEL__:!0});function i_(e,t,r){const n=r.config.validateStatus;!r.status||!n||n(r.status)?e(r):t(new it("Request failed with status code "+r.status,[it.ERR_BAD_REQUEST,it.ERR_BAD_RESPONSE][Math.floor(r.status/100)-4],r.config,r.request,r))}function uR(e){const t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||""}function cR(e,t){e=e||10;const r=new Array(e),n=new Array(e);let o=0,a=0,i;return t=t!==void 0?t:1e3,function(f){const g=Date.now(),b=n[a];i||(i=g),r[o]=f,n[o]=g;let _=a,O=0;for(;_!==o;)O+=r[_++],_=_%e;if(o=(o+1)%e,o===a&&(a=(a+1)%e),g-i<t)return;const S=b&&g-b;return S?Math.round(O*1e3/S):void 0}}function dR(e,t){let r=0,n=1e3/t,o,a;const i=(g,b=Date.now())=>{r=b,o=null,a&&(clearTimeout(a),a=null),e.apply(null,g)};return[(...g)=>{const b=Date.now(),_=b-r;_>=n?i(g,b):(o=g,a||(a=setTimeout(()=>{a=null,i(o)},n-_)))},()=>o&&i(o)]}const Lc=(e,t,r=3)=>{let n=0;const o=cR(50,250);return dR(a=>{const i=a.loaded,u=a.lengthComputable?a.total:void 0,f=i-n,g=o(f),b=i<=u;n=i;const _={loaded:i,total:u,progress:u?i/u:void 0,bytes:f,rate:g||void 0,estimated:g&&u&&b?(u-i)/g:void 0,event:a,lengthComputable:u!=null,[t?"download":"upload"]:!0};e(_)},r)},Xv=(e,t)=>{const r=e!=null;return[n=>t[0]({lengthComputable:r,total:e,loaded:n}),t[1]]},Jv=e=>(...t)=>ye.asap(()=>e(...t)),fR=rn.hasStandardBrowserEnv?function(){const t=rn.navigator&&/(msie|trident)/i.test(rn.navigator.userAgent),r=document.createElement("a");let n;function o(a){let i=a;return t&&(r.setAttribute("href",i),i=r.href),r.setAttribute("href",i),{href:r.href,protocol:r.protocol?r.protocol.replace(/:$/,""):"",host:r.host,search:r.search?r.search.replace(/^\?/,""):"",hash:r.hash?r.hash.replace(/^#/,""):"",hostname:r.hostname,port:r.port,pathname:r.pathname.charAt(0)==="/"?r.pathname:"/"+r.pathname}}return n=o(window.location.href),function(i){const u=ye.isString(i)?o(i):i;return u.protocol===n.protocol&&u.host===n.host}}():function(){return function(){return!0}}(),pR=rn.hasStandardBrowserEnv?{write(e,t,r,n,o,a){const i=[e+"="+encodeURIComponent(t)];ye.isNumber(r)&&i.push("expires="+new Date(r).toGMTString()),ye.isString(n)&&i.push("path="+n),ye.isString(o)&&i.push("domain="+o),a===!0&&i.push("secure"),document.cookie=i.join("; ")},read(e){const t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove(e){this.write(e,"",Date.now()-864e5)}}:{write(){},read(){return null},remove(){}};function hR(e){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}function gR(e,t){return t?e.replace(/\/?\/$/,"")+"/"+t.replace(/^\/+/,""):e}function l_(e,t){return e&&!hR(t)?gR(e,t):t}const Zv=e=>e instanceof Ln?{...e}:e;function Ga(e,t){t=t||{};const r={};function n(g,b,_){return ye.isPlainObject(g)&&ye.isPlainObject(b)?ye.merge.call({caseless:_},g,b):ye.isPlainObject(b)?ye.merge({},b):ye.isArray(b)?b.slice():b}function o(g,b,_){if(ye.isUndefined(b)){if(!ye.isUndefined(g))return n(void 0,g,_)}else return n(g,b,_)}function a(g,b){if(!ye.isUndefined(b))return n(void 0,b)}function i(g,b){if(ye.isUndefined(b)){if(!ye.isUndefined(g))return n(void 0,g)}else return n(void 0,b)}function u(g,b,_){if(_ in t)return n(g,b);if(_ in e)return n(void 0,g)}const f={url:a,method:a,data:a,baseURL:i,transformRequest:i,transformResponse:i,paramsSerializer:i,timeout:i,timeoutMessage:i,withCredentials:i,withXSRFToken:i,adapter:i,responseType:i,xsrfCookieName:i,xsrfHeaderName:i,onUploadProgress:i,onDownloadProgress:i,decompress:i,maxContentLength:i,maxBodyLength:i,beforeRedirect:i,transport:i,httpAgent:i,httpsAgent:i,cancelToken:i,socketPath:i,responseEncoding:i,validateStatus:u,headers:(g,b)=>o(Zv(g),Zv(b),!0)};return ye.forEach(Object.keys(Object.assign({},e,t)),function(b){const _=f[b]||o,O=_(e[b],t[b],b);ye.isUndefined(O)&&_!==u||(r[b]=O)}),r}const s_=e=>{const t=Ga({},e);let{data:r,withXSRFToken:n,xsrfHeaderName:o,xsrfCookieName:a,headers:i,auth:u}=t;t.headers=i=Ln.from(i),t.url=r_(l_(t.baseURL,t.url),e.params,e.paramsSerializer),u&&i.set("Authorization","Basic "+btoa((u.username||"")+":"+(u.password?unescape(enco Line truncated
`+a.map(ry).join(`
`):" "+ry(a[0]):"as no adapter specified";throw new it("There is no suitable adapter to dispatch the request "+i,"ERR_NOT_SUPPORT")}return n},adapters:m0};function bp(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new hl(null,e)}function ny(e){return bp(e),e.headers=Ln.from(e.headers),e.data=mp.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),d_.getAdapter(e.adapter||eg.adapter)(e).then(function(n){return bp(e),n.data=mp.call(e,e.transformResponse,n),n.headers=Ln.from(n.headers),n},function(n){return a_(n)||(bp(e),n&&n.response&&(n.response.data=mp.call(e,e.transformResponse,n.response),n.response.headers=Ln.from(n.response.headers))),Promise.reject(n)})}const f_="1.7.5",tg={};["object","boolean","number","function","string","symbol"].forEach((e,t)=>{tg[e]=function(n){return typeof n===e||"a"+(t<1?"n ":" ")+e}});const oy={};tg.transitional=function(t,r,n){function o(a,i){return"[Axios v"+f_+"] Transitional option '"+a+"'"+i+(n?". "+n:"")}return(a,i,u)=>{if(t===!1)throw new it(o(i," has been removed"+(r?" in "+r:"")),it.ERR_DEPRECATED);return r&&!oy[i]&&(oy[i]=!0,console.warn(o(i," has been deprecated since v"+r+" and will be removed in the near future"))),t?t(a,i,u):!0}};function PR(e,t,r){if(typeof e!="object")throw new it("options must be an object",it.ERR_BAD_OPTION_VALUE);const n=Object.keys(e);let o=n.length;for(;o-- >0;){const a=n[o],i=t[a];if(i){const u=e[a],f=u===void 0||i(u,a,e);if(f!==!0)throw new it("option "+a+" must be "+f,it.ERR_BAD_OPTION_VALUE);continue}if(r!==!0)throw new it("Unknown option "+a,it.ERR_BAD_OPTION)}}const b0={assertOptions:PR,validators:tg},$o=b0.validators;class zc{constructor(t){this.defaults=t,this.interceptors={request:new Yv,response:new Yv}}async request(t,r){try{return await this._request(t,r)}catch(n){if(n instanceof Error){let o;Error.captureStackTrace?Error.captureStackTrace(o={}):o=new Error;const a=o.stack?o.stack.replace(/^.+\n/,""):"";try{n.stack?a&&!String(n.stack).endsWith(a.replace(/^.+\n.+\n/,""))&&(n.stack+=`
`+a):n.stack=a}catch{}}throw n}}_request(t,r){typeof t=="string"?(r=r||{},r.url=t):r=t||{},r=Ga(this.defaults,r);const{transitional:n,paramsSerializer:o,headers:a}=r;n!==void 0&&b0.assertOptions(n,{silentJSONParsing:$o.transitional($o.boolean),forcedJSONParsing:$o.transitional($o.boolean),clarifyTimeoutError:$o.transitional($o.boolean)},!1),o!=null&&(ye.isFunction(o)?r.paramsSerializer={serialize:o}:b0.assertOptions(o,{encode:$o.function,serialize:$o.function},!0)),r.method=(r.method||this.defaults.method||"get").toLowerCase();let i=a&&ye.merge(a.common,a[r.method]);a&&ye.forEach(["delete","get","head","post","put","patch","common"],C=>{delete a[C]}),r.headers=Ln.concat(i,a);const u=[];let f=!0;this.interceptors.request.forEach(function(w){typeof w.runWhen=="function"&&w.runWhen(r)===!1||(f=f&&w.synchronous,u.unshift(w.fulfilled,w.rejected))});const g=[];this.interceptors.response.forEach(function(w){g.push(w.fulfilled,w.rejected)});let b,_=0,O;if(!f){const C=[ny.bind(this),void 0];for(C.unshift.apply(C,u),C.push.apply(C,g),O=C.length,b=Promise.resolve(r);_<O;)b=b.then(C[_++],C[_++]);return b}O=u.length;let S=r;for(_=0;_<O;){const C=u[_++],w=u[_++];try{S=C(S)}catch(m){w.call(this,m);break}}try{b=ny.call(this,S)}catch(C){return Promise.reject(C)}for(_=0,O=g.length;_<O;)b=b.then(g[_++],g[_++]);return b}getUri(t){t=Ga(this.defaults,t);const r=l_(t.baseURL,t.url);return r_(r,t.params,t.paramsSerializer)}}ye.forEach(["delete","get","head","options"],function(t){zc.prototype[t]=function(r,n){return this.request(Ga(n||{},{method:t,url:r,data:(n||{}).data}))}});ye.forEach(["post","put","patch"],function(t){function r(n){return function(a,i,u){return this.request(Ga(u||{},{method:t,headers:n?{"Content-Type":"multipart/form-data"}:{},url:a,data:i}))}}zc.prototype[t]=r(),zc.prototype[t+"Form"]=r(!0)});const cc=zc;class rg{constructor(t){if(typeof t!="function")throw new TypeError("executor must be a function.");let r;this.promise=new Promise(function(a){r=a});const n=this;this.promise.then(o=>{if(!n._listeners)return;let a=n._listeners.length;for(;a-- >0;)n._listeners[a](o);n._listeners=null}),this.promise.then=o=>{let a;const i=new Promise(u=>{n.subscribe(u),a=u}).then(o);return i.cancel=function(){n.unsubscribe(a)},i},t(function(a,i,u){n.reason||(n.reason=new hl(a,i,u),r(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(t){if(this.reason){t(this.reason);return}this._listeners?this._listeners.push(t):this._listeners=[t]}unsubscribe(t){if(!this._listeners)return;const r=this._listeners.indexOf(t);r!==-1&&this._listeners.splice(r,1)}static source(){let t;return{token:new rg(function(o){t=o}),cancel:t}}}const kR=rg;function ER(e){return function(r){return e.apply(null,r)}}function NR(e){return ye.isObject(e)&&e.isAxiosError===!0}const v0={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(v0).forEach(([e,t])=>{v0[t]=e});const jR=v0;function p_(e){const t=new cc(e),r=Hx(cc.prototype.request,t);return ye.extend(r,cc.prototype,t,{allOwnKeys:!0}),ye.extend(r,t,null,{allOwnKeys:!0}),r.create=function(o){return p_(Ga(e,o))},r}const or=p_(eg);or.Axios=cc;or.CanceledError=hl;or.CancelToken=kR;or.isCancel=a_;or.VERSION=f_;or.toFormData=Ld;or.AxiosError=it;or.Cancel=or.CanceledError;or.all=function(t){return Promise.all(t)};or.spread=ER;or.isAxiosError=NR;or.mergeConfig=Ga;or.AxiosHeaders=Ln;or.formToJSON=e=>o_(ye.isHTMLForm(e)?new FormData(e):e);or.getAdapter=d_.getAdapter;or.HttpStatusCode=jR;or.default=or;const at=or;var y0={},h_={exports:{}},sn={},g_={exports:{}},m_={};/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/(function(e){function t($,z){var H=$.length;$.push(z);e:for(;0<H;){var G=H-1>>>1,X=$[G];if(0<o(X,z))$[G]=z,$[H]=X,H=G;else break e}}function r($){return $.length===0?null:$[0]}function n($){if($.length===0)return null;var z=$[0],H=$.pop();if(H!==z){$[0]=H;e:for(var G=0,X=$.length,ie=X>>>1;G<ie;){var fe=2*(G+1)-1,pe=$[fe],oe=fe+1,se=$[oe];if(0>o(pe,H))oe<X&&0>o(se,pe)?($[G]=se,$[oe]=H,G=oe):($[G]=pe,$[fe]=H,G=fe);else if(oe<X&&0>o(se,H))$[G]=se,$[oe]=H,G=oe;else break e}}return z}function o($,z){var H=$.sortIndex-z.sortIndex;return H!==0?H:$.id-z.id}if(typeof performance=="object"&&typeof performance.now=="function"){var a=performance;e.unstable_now=function(){return a.now()}}else{var i=Date,u=i.now();e.unstable_now=function(){return i.now()-u}}var f=[],g=[],b=1,_=null,O=3,S=!1,C=!1,w=!1,m=typeof setTimeout=="function"?setTimeout:null,d=typeof clearTimeout=="function"?clearTimeout:null,l=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function c($){for(var z=r(g);z!==null;){if(z.callback===null)n(g);else if(z.startTime<=$)n(g),z.sortIndex=z.expirationTime,t(f,z);else break;z=r(g)}}function s($){if(w=!1,c($),!C)if(r(f)!==null)C=!0,U(h);else{var z=r(g);z!==null&&Y(s,z.startTime-$)}}function h($,z){C=!1,w&&(w=!1,d(E),E=-1),S=!0;var H=O;try{for(c(z),_=r(f);_!==null&&(!(_.expirationTime>z)||$&&!R());){var G=_.callback;if(typeof G=="function"){_.callback=null,O=_.priorityLevel;var X=G(_.expirationTime<=z);z=e.unstable_now(),typeof X=="function"?_.callback=X:_===r(f)&&n(f),c(z)}else n(f);_=r(f)}if(_!==null)var ie=!0;else{var fe=r(g);fe!==null&&Y(s,fe.startTime-z),ie=!1}return ie}finally{_=null,O=H,S=!1}}var v=!1,y=null,E=-1,P=5,N=-1;function R(){return!(e.unstable_now()-N<P)}function M(){if(y!==null){var $=e.unstable_now();N=$;var z=!0;try{z=y(!0,$)}finally{z?D():(v=!1,y=null)}}else v=!1}var D;if(typeof l=="function")D=function(){l(M)};else if(typeof MessageChannel<"u"){var L=new MessageChannel,W=L.port2;L.port1.onmessage=M,D=function(){W.postMessage(null)}}else D=function(){m(M,0)};function U($){y=$,v||(v=!0,D())}function Y($,z){E=m(function(){$(e.unstable_now())},z)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function($){$.callback=null},e.unstable_continueExecution=function(){C||S||(C=!0,U(h))},e.unstable_forceFrameRate=function($){0>$||125<$?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):P=0<$?Math.floor(1e3/$):5},e.unstable_getCurrentPriorityLevel=function(){return O},e.unstable_getFirstCallbackNode=function(){return r(f)},e.unstable_next=function($){switch(O){case 1:case 2:case 3:var z=3;break;default:z=O}var H=O;O=z;try{return $()}finally{O=H}},e.unstable_pauseExecution=function(){},e.unstable_requestPaint=function(){},e.unstable_runWithPriority=function($,z){switch($){case 1:case 2:case 3:case 4:case 5:break;default:$=3}var H=O;O=$;try{return z()}finally{O=H}},e.unstable_scheduleCallback=function($,z,H){var G=e.unstable_now();switch(typeof H=="object"&&H!==null?(H=H.delay,H=typeof H=="number"&&0<H?G+H:G):H=G,$){case 1:var X=-1;break;case 2:X=250;break;case 5:X=1073741823;break;case 4:X=1e4;break;default:X=5e3}return X=H+X,$={id:b++,callback:z,priorityLevel:$,startTime:H,expirationTime:X,sortIndex:-1},H>G?($.sortIndex=H,t(g,$),r(f)===null&&$===r(g)&&(w?(d(E),E=-1):w=!0,Y(s,H-G))):($.sortIndex=X,t(f,$),C||S||(C=!0,U(h))),$},e.unstable_shouldYield=R,e.unstable_wrapCallback=function($){var z=O;return function(){var H=O;O=z;try{return $.apply(this,arguments)}finally{O=H}}}})(m_);g_.exports=m_;var RR=g_.exports;/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var b_=j,an=RR;function ke(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,r=1;r<arguments.length;r++)t+="&args[]="+encodeURIComponent(arguments[r]);return"Minified React error #"+e+"; visit "+t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}var v_=new Set,Cs={};function ni(e,t){Qi(e,t),Qi(e+"Capture",t)}function Qi(e,t){for(Cs[e]=t,e=0;e<t.length;e++)v_.add(t[e])}var Po=!(typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),w0=Object.prototype.hasOwnProperty,AR=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,ay={},iy={};function MR(e){return w0.call(iy,e)?!0:w0.call(ay,e)?!1:AR.test(e)?iy[e]=!0:(ay[e]=!0,!1)}function IR(e,t,r,n){if(r!==null&&r.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return n?!1:r!==null?!r.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function DR(e,t,r,n){if(t===null||typeof t>"u"||IR(e,t,r,n))return!0;if(n)return!1;if(r!==null)switch(r.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function Lr(e,t,r,n,o,a,i){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=n,this.attributeNamespace=o,this.mustUseProperty=r,this.propertyName=e,this.type=t,this.sanitizeURL=a,this.removeEmptyString=i}var vr={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){vr[e]=new Lr(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];vr[t]=new Lr(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){vr[e]=new Lr(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){vr[e]=new Lr(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){vr[e]=new Lr(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){vr[e]=new Lr(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){vr[e]=new Lr(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){vr[e]=new Lr(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){vr[e]=new Lr(e,5,!1,e.toLowerCase(),null,!1,!1)});var ng=/[\-:]([a-z])/g;function og(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(ng,og);vr[t]=new Lr(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(ng,og);vr[t]=new Lr(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(ng,og);vr[t]=new Lr(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){vr[e]=new Lr(e,1,!1,e.toLowerCase(),null,!1,!1)});vr.xlinkHref=new Lr("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){vr[e]=new Lr(e,1,!1,e.toLowerCase(),null,!0,!0)});function ag(e,t,r,n){var o=vr.hasOwnProperty(t)?vr[t]:null;(o!==null?o.type!==0:n||!(2<t.length)||t[0]!=="o"&&t[0]!== Line truncated
`+vp+e}var yp=!1;function wp(e,t){if(!e||yp)return"";yp=!0;var r=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{if(t)if(t=function(){throw Error()},Object.defineProperty(t.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(t,[])}catch(g){var n=g}Reflect.construct(e,[],t)}else{try{t.call()}catch(g){n=g}e.call(t.prototype)}else{try{throw Error()}catch(g){n=g}e()}}catch(g){if(g&&n&&typeof g.stack=="string"){for(var o=g.stack.split(`
`),a=n.stack.split(`
`),i=o.length-1,u=a.length-1;1<=i&&0<=u&&o[i]!==a[u];)u--;for(;1<=i&&0<=u;i--,u--)if(o[i]!==a[u]){if(i!==1||u!==1)do if(i--,u--,0>u||o[i]!==a[u]){var f=`
`+o[i].replace(" at new "," at ");return e.displayName&&f.includes("<anonymous>")&&(f=f.replace("<anonymous>",e.displayName)),f}while(1<=i&&0<=u);break}}}finally{yp=!1,Error.prepareStackTrace=r}return(e=e?e.displayName||e.name:"")?ts(e):""}function LR(e){switch(e.tag){case 5:return ts(e.type);case 16:return ts("Lazy");case 13:return ts("Suspense");case 19:return ts("SuspenseList");case 0:case 2:case 15:return e=wp(e.type,!1),e;case 11:return e=wp(e.type.render,!1),e;case 1:return e=wp(e.type,!0),e;default:return""}}function S0(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Oi:return"Fragment";case Ti:return"Portal";case x0:return"Profiler";case ig:return"StrictMode";case _0:return"Suspense";case C0:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case w_:return(e.displayName||"Context")+".Consumer";case y_:return(e._context.displayName||"Context")+".Provider";case lg:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case sg:return t=e.displayName||null,t!==null?t:S0(e.type)||"Memo";case Xo:t=e._payload,e=e._init;try{return S0(e(t))}catch{}}return null}function FR(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return S0(t);case 8:return t===ig?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function ma(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function __(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function zR(e){var t=__(e)?"checked":"value",r=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),n=""+e[t];if(!e.hasOwnProperty(t)&&typeof r<"u"&&typeof r.get=="function"&&typeof r.set=="function"){var o=r.get,a=r.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return o.call(this)},set:function(i){n=""+i,a.call(this,i)}}),Object.defineProperty(e,t,{enumerable:r.enumerable}),{getValue:function(){return n},setValue:function(i){n=""+i},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Mu(e){e._valueTracker||(e._valueTracker=zR(e))}function C_(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var r=t.getValue(),n="";return e&&(n=__(e)?e.checked?"true":"false":e.value),e=n,e!==r?(t.setValue(e),!0):!1}function Bc(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function T0(e,t){var r=t.checked;return $t({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:r??e._wrapperState.initialChecked})}function sy(e,t){var r=t.defaultValue==null?"":t.defaultValue,n=t.checked!=null?t.checked:t.defaultChecked;r=ma(t.value!=null?t.value:r),e._wrapperState={initialChecked:n,initialValue:r,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function S_(e,t){t=t.checked,t!=null&&ag(e,"checked",t,!1)}function O0(e,t){S_(e,t);var r=ma(t.value),n=t.type;if(r!=null)n==="number"?(r===0&&e.value===""||e.value!=r)&&(e.value=""+r):e.value!==""+r&&(e.value=""+r);else if(n==="submit"||n==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?P0(e,t.type,r):t.hasOwnProperty("defaultValue")&&P0(e,t.type,ma(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function uy(e,t,r){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var n=t.type;if(!(n!=="submit"&&n!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,r||t===e.value||(e.value=t),e.defaultValue=t}r=e.name,r!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,r!==""&&(e.name=r)}function P0(e,t,r){(t!=="number"||Bc(e.ownerDocument)!==e)&&(r==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+r&&(e.defaultValue=""+r))}var rs=Array.isArray;function zi(e,t,r,n){if(e=e.options,t){t={};for(var o=0;o<r.length;o++)t["$"+r[o]]=!0;for(r=0;r<e.length;r++)o=t.hasOwnProperty("$"+e[r].value),e[r].selected!==o&&(e[r].selected=o),o&&n&&(e[r].defaultSelected=!0)}else{for(r=""+ma(r),t=null,o=0;o<e.length;o++){if(e[o].value===r){e[o].selected=!0,n&&(e[o].defaultSelected=!0);return}t!==null||e[o].disabled||(t=e[o])}t!==null&&(t.selected=!0)}}function k0(e,t){if(t.dan Line truncated
`).replace(YA,"")}function Uu(e,t,r){if(t=Ry(t),Ry(e)!==t&&r)throw Error(ke(425))}function Kc(){}var V0=null,U0=null;function W0(e,t){return e==="textarea"||e==="noscript"||typeof t.children=="string"||typeof t.children=="number"||typeof t.dangerouslySetInnerHTML=="object"&&t.dangerouslySetInnerHTML!==null&&t.dangerouslySetInnerHTML.__html!=null}var H0=typeof setTimeout=="function"?setTimeout:void 0,QA=typeof clearTimeout=="function"?clearTimeout:void 0,Ay=typeof Promise=="function"?Promise:void 0,XA=typeof queueMicrotask=="function"?queueMicrotask:typeof Ay<"u"?function(e){return Ay.resolve(null).then(e).catch(JA)}:H0;function JA(e){setTimeout(function(){throw e})}function Ap(e,t){var r=t,n=0;do{var o=r.nextSibling;if(e.removeChild(r),o&&o.nodeType===8)if(r=o.data,r==="/$"){if(n===0){e.removeChild(o),ks(t);return}n--}else r!=="$"&&r!=="$?"&&r!=="$!"||n++;r=o}while(r);ks(t)}function sa(e){for(;e!=null;e=e.nextSibling){var t=e.nodeType;if(t===1||t===3)break;if(t===8){if(t=e.data,t==="$"||t==="$!"||t==="$?")break;if(t==="/$")return null}}return e}function My(e){e=e.previousSibling;for(var t=0;e;){if(e.nodeType===8){var r=e.data;if(r==="$"||r==="$!"||r==="$?"){if(t===0)return e;t--}else r==="/$"&&t++}e=e.previousSibling}return null}var ml=Math.random().toString(36).slice(2),no="__reactFiber$"+ml,As="__reactProps$"+ml,ko="__reactContainer$"+ml,$0="__reactEvents$"+ml,ZA="__reactListeners$"+ml,eM="__reactHandles$"+ml;function Ba(e){var t=e[no];if(t)return t;for(var r=e.parentNode;r;){if(t=r[ko]||r[no]){if(r=t.alternate,t.child!==null||r!==null&&r.child!==null)for(e=My(e);e!==null;){if(r=e[no])return r;e=My(e)}return t}e=r,r=e.parentNode}return null}function tu(e){return e=e[no]||e[ko],!e||e.tag!==5&&e.tag!==6&&e.tag!==13&&e.tag!==3?null:e}function Ni(e){if(e.tag===5||e.tag===6)return e.stateNode;throw Error(ke(33))}function Hd(e){return e[As]||null}var q0=[],ji=-1;function wa(e){return{current:e}}function It(e){0>ji||(e.current=q0[ji],q0[ji]=null,ji--)}function kt(e,t){ji++,q0[ji]=e.current,e.current=t}var ba={},Er=wa(ba),$r=wa(!1),Ka=ba;function Xi(e,t){var r=e.type.contextTypes;if(!r)return ba;var n=e.stateNode;if(n&&n.__reactInternalMemoizedUnmaskedChildContext===t)return n.__reactInternalMemoizedMaskedChildContext;var o={},a;for(a in r)o[a]=t[a];return n&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=o),o}function qr(e){return e=e.childContextTypes,e!=null}function Yc(){It($r),It(Er)}function Iy(e,t,r){if(Er.current!==ba)throw Error(ke(168));kt(Er,t),kt($r,r)}function fC(e,t,r){var n=e.stateNode;if(t=t.childContextTypes,typeof n.getChildContext!="function")return r;n=n.getChildContext();for(var o in n)if(!(o in t))throw Error(ke(108,FR(e)||"Unknown",o));return $t({},r,n)}function Qc(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||ba,Ka=Er.current,kt(Er,e),kt($r,$r.current),!0}function Dy(e,t,r){var n=e.stateNode;if(!n)throw Error(ke(169));r?(e=fC(e,t,Ka),n.__reactInternalMemoizedMergedChildContext=e,It($r),It(Er),kt(Er,e)):It($r),kt($r,r)}var yo=null,$d=!1,Mp=!1;function pC(e){yo===null?yo=[e]:yo.push(e)}function tM(e){$d=!0,pC(e)}function xa(){if(!Mp&&yo!==null){Mp=!0;var e=0,t=xt;try{var r=yo;for(xt=1;e<r.length;e++){var n=r[e];do n=n(!0);while(n!==null)}yo=null,$d=!1}catch(o){throw yo!==null&&(yo=yo.slice(e+1)),F_(cg,xa),o}finally{xt=t,Mp=!1}}return null}var Ri=[],Ai=0,Xc=null,Jc=0,bn=[],vn=0,Ya=null,wo=1,xo="";function Da(e,t){Ri[Ai++]=Jc,Ri[Ai++]=Xc,Xc=e,Jc=t}function hC(e,t,r){bn[vn++]=wo,bn[vn++]=xo,bn[vn++]=Ya,Ya=e;var n=wo;e=xo;var o=32-Fn(n)-1;n&=~(1<<o),r+=1;var a=32-Fn(t)+o;if(30<a){var i=o-o%5;a=(n&(1<<i)-1).toString(32),n>>=i,o-=i,wo=1<<32-Fn(t)+o|r<<o|n,xo=a+e}else wo=1<<a|r<<o|n,xo=e}function yg(e){e.return!==null&&(Da(e,1),hC(e,1,0))}function wg(e){for(;e===Xc;)Xc=Ri[--Ai],Ri[Ai]=null,Jc=Ri[--Ai],Ri[Ai]=null;for(;e===Ya;)Ya=bn[--vn],bn[vn]=null,xo=bn[--vn],bn[vn]=null,wo=bn[--vn],bn[vn]=null}var nn=null,en=null,Ft=!1,Mn=null;function gC(e,t){var r=wn(5,null,null,0);r.elementType="DELETED",r.stateNode=t,r.return=e,t=e.deletions,t===null?(e.deletions=[r],e.flags|=16):t.push(r)}function Ly(e,t){switch(e.tag){case 5:var r=e.type;return t=t.nodeType!==1||r.toLowerCase()!==t.nodeName.toLowerCase()?null:t,t!==null?(e.stateNode=t,nn=e,en=sa(t.firstChild),!0):!1;case 6:return t=e.pendingProps===""||t.nodeType!==3?null:t,t!==null?(e.stateNode=t,nn=e,en=null,!0):!1;case 13:return t=t.nodeType!==8?null:t,t!==null?(r=Ya!==null?{id:wo,overflow:xo}:null,e.memoizedState={dehydrated:t,treeContext:r,retryLane:1073741824},r=wn(18,null,null,0),r.stateNode=t,r.return=e,e.child=r,nn=e,en=null,!0):!1;default:return!1}}function G0(e){return(e.mode&1)!==0&&(e.flags&128)===0}function K0(e){if(Ft){var t=en;if(t){var r=t;if(!Ly(e,t)){if(G0(e))throw Error(ke(418));t=sa(r.nextSibling);var n=nn;t&&Ly(e,t)?gC(n,r):(e.flags=e.flags&-4097|2,Ft=!1,nn=e)}}else{if(G0(e))throw Error(ke(418));e.flags=e.flags&-4097|2,Ft=!1,nn=e}}}function Fy(e){ Line truncated
Error generating stack: `+a.message+`
`+a.stack}return{value:e,source:t,stack:o,digest:null}}function zp(e,t,r){return{value:e,source:null,stack:r??null,digest:t??null}}function J0(e,t){try{console.error(t.value)}catch(r){setTimeout(function(){throw r})}}var cM=typeof WeakMap=="function"?WeakMap:Map;function WC(e,t,r){r=So(-1,r),r.tag=3,r.payload={element:null};var n=t.value;return r.callback=function(){id||(id=!0,sh=n),J0(e,t)},r}function HC(e,t,r){r=So(-1,r),r.tag=3;var n=e.type.getDerivedStateFromError;if(typeof n=="function"){var o=t.value;r.payload=function(){return n(o)},r.callback=function(){J0(e,t)}}var a=e.stateNode;return a!==null&&typeof a.componentDidCatch=="function"&&(r.callback=function(){J0(e,t),typeof n!="function"&&(ca===null?ca=new Set([this]):ca.add(this));var i=t.stack;this.componentDidCatch(t.value,{componentStack:i!==null?i:""})}),r}function qy(e,t,r){var n=e.pingCache;if(n===null){n=e.pingCache=new cM;var o=new Set;n.set(t,o)}else o=n.get(t),o===void 0&&(o=new Set,n.set(t,o));o.has(r)||(o.add(r),e=SM.bind(null,e,t,r),t.then(e,e))}function Gy(e){do{var t;if((t=e.tag===13)&&(t=e.memoizedState,t=t!==null?t.dehydrated!==null:!0),t)return e;e=e.return}while(e!==null);return null}function Ky(e,t,r,n,o){return e.mode&1?(e.flags|=65536,e.lanes=o,e):(e===t?e.flags|=65536:(e.flags|=128,r.flags|=131072,r.flags&=-52805,r.tag===1&&(r.alternate===null?r.tag=17:(t=So(-1,1),t.tag=2,ua(r,t,1))),r.lanes|=1),e)}var dM=Ao.ReactCurrentOwner,Wr=!1;function Mr(e,t,r,n){t.child=e===null?_C(t,null,r,n):Zi(t,e.child,r,n)}function Yy(e,t,r,n,o){r=r.render;var a=t.ref;return Wi(t,o),n=jg(e,t,r,n,a,o),r=Rg(),e!==null&&!Wr?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,No(e,t,o)):(Ft&&r&&yg(t),t.flags|=1,Mr(e,t,n,o),t.child)}function Qy(e,t,r,n,o){if(e===null){var a=r.type;return typeof a=="function"&&!Ug(a)&&a.defaultProps===void 0&&r.compare===null&&r.defaultProps===void 0?(t.tag=15,t.type=a,$C(e,t,a,n,o)):(e=wc(r.type,null,n,t,t.mode,o),e.ref=t.ref,e.return=t,t.child=e)}if(a=e.child,!(e.lanes&o)){var i=a.memoizedProps;if(r=r.compare,r=r!==null?r:Ns,r(i,n)&&e.ref===t.ref)return No(e,t,o)}return t.flags|=1,e=fa(a,n),e.ref=t.ref,e.return=t,t.child=e}function $C(e,t,r,n,o){if(e!==null){var a=e.memoizedProps;if(Ns(a,n)&&e.ref===t.ref)if(Wr=!1,t.pendingProps=n=a,(e.lanes&o)!==0)e.flags&131072&&(Wr=!0);else return t.lanes=e.lanes,No(e,t,o)}return Z0(e,t,r,n,o)}function qC(e,t,r){var n=t.pendingProps,o=n.children,a=e!==null?e.memoizedState:null;if(n.mode==="hidden")if(!(t.mode&1))t.memoizedState={baseLanes:0,cachePool:null,transitions:null},kt(Di,Zr),Zr|=r;else{if(!(r&1073741824))return e=a!==null?a.baseLanes|r:r,t.lanes=t.childLanes=1073741824,t.memoizedState={baseLanes:e,cachePool:null,transitions:null},t.updateQueue=null,kt(Di,Zr),Zr|=e,null;t.memoizedState={baseLanes:0,cachePool:null,transitions:null},n=a!==null?a.baseLanes:r,kt(Di,Zr),Zr|=n}else a!==null?(n=a.baseLanes|r,t.memoizedState=null):n=r,kt(Di,Zr),Zr|=n;return Mr(e,t,o,r),t.child}function GC(e,t){var r=t.ref;(e===null&&r!==null||e!==null&&e.ref!==r)&&(t.flags|=512,t.flags|=2097152)}function Z0(e,t,r,n,o){var a=qr(r)?Ka:Er.current;return a=Xi(t,a),Wi(t,o),r=jg(e,t,r,n,a,o),n=Rg(),e!==null&&!Wr?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,No(e,t,o)):(Ft&&n&&yg(t),t.flags|=1,Mr(e,t,r,o),t.child)}function Xy(e,t,r,n,o){if(qr(r)){var a=!0;Qc(t)}else a=!1;if(Wi(t,o),t.stateNode===null)bc(e,t),wC(t,r,n),X0(t,r,n,o),n=!0;else if(e===null){var i=t.stateNode,u=t.memoizedProps;i.props=u;var f=i.context,g=r.contextType;typeof g=="object"&&g!==null?g=Sn(g):(g=qr(r)?Ka:Er.current,g=Xi(t,g));var b=r.getDerivedStateFromProps,_=typeof b=="function"||typeof i.getSnapshotBeforeUpdate=="function";_||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(u!==n||f!==g)&&Uy(t,i,n,g),Jo=!1;var O=t.memoizedState;i.state=O,td(t,n,i,o),f=t.memoizedState,u!==n||O!==f||$r.current||Jo?(typeof b=="function"&&(Q0(t,r,b,n),f=t.memoizedState),(u=Jo||Vy(t,r,u,n,O,f,g))?(_||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount()),typeof i.componentDidMount=="function"&&(t.flags|=4194308)):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),t.memoizedProps=n,t.memoizedState=f),i.props=n,i.state=f,i.context=g,n=u):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),n=!1)}else{i=t.stateNode,vC(e,t),u=t.memoizedProps,g=t.type===t.elementType?u:jn(t.type,u),i.props=g,_=t.pendingProps,O=i.context,f=r.contextType,typeof f=="object"&&f!==null?f=Sn(f):(f=qr(r)?Ka:Er.current,f=Xi(t,f));var S=r.getDerivedStateFromProps;(b=typeof S=="function"||typeof i.getSnapshotBeforeUpdate=="function")||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(u!==_||O!==f)&&Uy(t,i,n,f),Jo=!1,O=t.memoizedState,i.state=O,td(t,n,i,o);var C=t.memoizedS Line truncated
* @remix-run/router v1.10.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/function Bs(){return Bs=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},Bs.apply(this,arguments)}var oa;(function(e){e.Pop="POP",e.Push="PUSH",e.Replace="REPLACE"})(oa||(oa={}));const dw="popstate";function IM(e){e===void 0&&(e={});function t(n,o){let{pathname:a,search:i,hash:u}=n.location;return fh("",{pathname:a,search:i,hash:u},o.state&&o.state.usr||null,o.state&&o.state.key||"default")}function r(n,o){return typeof o=="string"?o:cd(o)}return LM(t,r,null,e)}function Ht(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function Gg(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function DM(){return Math.random().toString(36).substr(2,8)}function fw(e,t){return{usr:e.state,key:e.key,idx:t}}function fh(e,t,r,n){return r===void 0&&(r=null),Bs({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?bl(t):t,{state:r,key:t&&t.key||n||DM()})}function cd(e){let{pathname:t="/",search:r="",hash:n=""}=e;return r&&r!=="?"&&(t+=r.charAt(0)==="?"?r:"?"+r),n&&n!=="#"&&(t+=n.charAt(0)==="#"?n:"#"+n),t}function bl(e){let t={};if(e){let r=e.indexOf("#");r>=0&&(t.hash=e.substr(r),e=e.substr(0,r));let n=e.indexOf("?");n>=0&&(t.search=e.substr(n),e=e.substr(0,n)),e&&(t.pathname=e)}return t}function LM(e,t,r,n){n===void 0&&(n={});let{window:o=document.defaultView,v5Compat:a=!1}=n,i=o.history,u=oa.Pop,f=null,g=b();g==null&&(g=0,i.replaceState(Bs({},i.state,{idx:g}),""));function b(){return(i.state||{idx:null}).idx}function _(){u=oa.Pop;let m=b(),d=m==null?null:m-g;g=m,f&&f({action:u,location:w.location,delta:d})}function O(m,d){u=oa.Push;let l=fh(w.location,m,d);r&&r(l,m),g=b()+1;let c=fw(l,g),s=w.createHref(l);try{i.pushState(c,"",s)}catch(h){if(h instanceof DOMException&&h.name==="DataCloneError")throw h;o.location.assign(s)}a&&f&&f({action:u,location:w.location,delta:1})}function S(m,d){u=oa.Replace;let l=fh(w.location,m,d);r&&r(l,m),g=b();let c=fw(l,g),s=w.createHref(l);i.replaceState(c,"",s),a&&f&&f({action:u,location:w.location,delta:0})}function C(m){let d=o.location.origin!=="null"?o.location.origin:o.location.href,l=typeof m=="string"?m:cd(m);return Ht(d,"No window.location.(origin|href) available to create URL for href: "+l),new URL(l,d)}let w={get action(){return u},get location(){return e(o,i)},listen(m){if(f)throw new Error("A history only accepts one active listener");return o.addEventListener(dw,_),f=m,()=>{o.removeEventListener(dw,_),f=null}},createHref(m){return t(o,m)},createURL:C,encodeLocation(m){let d=C(m);return{pathname:d.pathname,search:d.search,hash:d.hash}},push:O,replace:S,go(m){return i.go(m)}};return w}var pw;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})(pw||(pw={}));function FM(e,t,r){r===void 0&&(r="/");let n=typeof t=="string"?bl(t):t,o=Vs(n.pathname||"/",r);if(o==null)return null;let a=b5(e);zM(a);let i=null;for(let u=0;i==null&&u<a.length;++u)i=KM(a[u],QM(o));return i}function b5(e,t,r,n){t===void 0&&(t=[]),r===void 0&&(r=[]),n===void 0&&(n="");let o=(a,i,u)=>{let f={relativePath:u===void 0?a.path||"":u,caseSensitive:a.caseSensitive===!0,childrenIndex:i,route:a};f.relativePath.startsWith("/")&&(Ht(f.relativePath.startsWith(n),'Absolute route path "'+f.relativePath+'" nested under path '+('"'+n+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),f.relativePath=f.relativePath.slice(n.length));let g=pa([n,f.relativePath]),b=r.concat(f);a.children&&a.children.length>0&&(Ht(a.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+g+'".')),b5(a.children,t,b,g)),!(a.path==null&&!a.index)&&t.push({path:g,score:qM(g,a.index),routesMeta:b})};return e.forEach((a,i)=>{var u;if(a.path===""||!((u=a.path)!=null&&u.includes("?")))o(a,i);else for(let f of v5(a.path))o(a,i,f)}),t}function v5(e){let t=e.split("/");if(t.length===0)return[];let[r,...n]=t,o=r.endsWith("?"),a=r.replace(/\?$/,"");if(n.length===0)return o?[a,""]:[a];let i=v5(n.join("/")),u=[];return u.push(...i.map(f=>f===""?a:[a,f].join("/"))),o&&u.push(...i),u.map(f=>e.startsWith("/")&&f===""?"/":f)}function zM(e){e.sort((t,r)=>t.score!==r.score?r.score-t.score:GM(t.routesMeta.map(n=>n.childrenIndex),r.routesMeta.map(n=>n.childrenIndex)))}const BM=/^:\w+$/,VM=3,UM=2,WM=1,HM=10,$M=-2,hw=e=>e==="*";function qM(e,t){let r=e.split("/"),n=r.length;return r.some(hw)&&(n+=$M),t&&(n+=UM),r.filter(o=>!hw(o)).reduce((o,a)=>o+(BM.test(a)?VM:a===""?WM:HM),n)}function GM(e,t){return e.length===t.length&&e.slice(0,-1).every((n,o)=>n===t[o])?e[e.length-1]-t[t.length-1]:0}function KM(e,t){let{routesMeta:r}=e,n={},o="/",a=[];for(let i=0;i<r.length;++i){let u=r[i],f=i===r.length-1,g=o==="/"?t:t.slice(o.length)||"/",b=ph({path:u.relativePath,caseSensitive:u.caseSensitive,end:f},g);if(!b)return null;Object.assign(n,b.paLine truncated
* React Router v6.17.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/function dd(){return dd=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},dd.apply(this,arguments)}const tf=j.createContext(null),w5=j.createContext(null),ai=j.createContext(null),rf=j.createContext(null),Mo=j.createContext({outlet:null,matches:[],isDataRoute:!1}),x5=j.createContext(null);function aI(e,t){let{relative:r}=t===void 0?{}:t;vl()||Ht(!1);let{basename:n,navigator:o}=j.useContext(ai),{hash:a,pathname:i,search:u}=nf(e,{relative:r}),f=i;return n!=="/"&&(f=i==="/"?n:pa([n,i])),o.createHref({pathname:f,search:u,hash:a})}function vl(){return j.useContext(rf)!=null}function _a(){return vl()||Ht(!1),j.useContext(rf).location}function _5(e){j.useContext(ai).static||j.useLayoutEffect(e)}function ii(){let{isDataRoute:e}=j.useContext(Mo);return e?vI():iI()}function iI(){vl()||Ht(!1);let e=j.useContext(tf),{basename:t,navigator:r}=j.useContext(ai),{matches:n}=j.useContext(Mo),{pathname:o}=_a(),a=JSON.stringify(Kg(n).map(f=>f.pathnameBase)),i=j.useRef(!1);return _5(()=>{i.current=!0}),j.useCallback(function(f,g){if(g===void 0&&(g={}),!i.current)return;if(typeof f=="number"){r.go(f);return}let b=Yg(f,JSON.parse(a),o,g.relative==="path");e==null&&t!=="/"&&(b.pathname=b.pathname==="/"?t:pa([t,b.pathname])),(g.replace?r.replace:r.push)(b,g.state,g)},[t,r,a,o,e])}function C5(){let{matches:e}=j.useContext(Mo),t=e[e.length-1];return t?t.params:{}}function nf(e,t){let{relative:r}=t===void 0?{}:t,{matches:n}=j.useContext(Mo),{pathname:o}=_a(),a=JSON.stringify(Kg(n).map(i=>i.pathnameBase));return j.useMemo(()=>Yg(e,JSON.parse(a),o,r==="path"),[e,a,o,r])}function lI(e,t){return sI(e,t)}function sI(e,t,r){vl()||Ht(!1);let{navigator:n}=j.useContext(ai),{matches:o}=j.useContext(Mo),a=o[o.length-1],i=a?a.params:{};a&&a.pathname;let u=a?a.pathnameBase:"/";a&&a.route;let f=_a(),g;if(t){var b;let w=typeof t=="string"?bl(t):t;u==="/"||(b=w.pathname)!=null&&b.startsWith(u)||Ht(!1),g=w}else g=f;let _=g.pathname||"/",O=u==="/"?_:_.slice(u.length)||"/",S=FM(e,{pathname:O}),C=pI(S&&S.map(w=>Object.assign({},w,{params:Object.assign({},i,w.params),pathname:pa([u,n.encodeLocation?n.encodeLocation(w.pathname).pathname:w.pathname]),pathnameBase:w.pathnameBase==="/"?u:pa([u,n.encodeLocation?n.encodeLocation(w.pathnameBase).pathname:w.pathnameBase])})),o,r);return t&&C?j.createElement(rf.Provider,{value:{location:dd({pathname:"/",search:"",hash:"",state:null,key:"default"},g),navigationType:oa.Pop}},C):C}function uI(){let e=bI(),t=nI(e)?e.status+" "+e.statusText:e instanceof Error?e.message:JSON.stringify(e),r=e instanceof Error?e.stack:null,o={padding:"0.5rem",backgroundColor:"rgba(200,200,200, 0.5)"},a=null;return j.createElement(j.Fragment,null,j.createElement("h2",null,"Unexpected Application Error!"),j.createElement("h3",{style:{fontStyle:"italic"}},t),r?j.createElement("pre",{style:o},r):null,a)}const cI=j.createElement(uI,null);class dI extends j.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,r){return r.location!==t.location||r.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error||r.error,location:r.location,revalidation:t.revalidation||r.revalidation}}componentDidCatch(t,r){console.error("React Router caught the following error during render",t,r)}render(){return this.state.error?j.createElement(Mo.Provider,{value:this.props.routeContext},j.createElement(x5.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function fI(e){let{routeContext:t,match:r,children:n}=e,o=j.useContext(tf);return o&&o.static&&o.staticContext&&(r.route.errorElement||r.route.ErrorBoundary)&&(o.staticContext._deepestRenderedBoundaryId=r.route.id),j.createElement(Mo.Provider,{value:t},n)}function pI(e,t,r){var n;if(t===void 0&&(t=[]),r===void 0&&(r=null),e==null){var o;if((o=r)!=null&&o.errors)e=r.matches;else return null}let a=e,i=(n=r)==null?void 0:n.errors;if(i!=null){let u=a.findIndex(f=>f.route.id&&(i==null?void 0:i[f.route.id]));u>=0||Ht(!1),a=a.slice(0,Math.min(a.length,u+1))}return a.reduceRight((u,f,g)=>{let b=f.route.id?i==null?void 0:i[f.route.id]:null,_=null;r&&(_=f.route.errorElement||cI);let O=t.concat(a.slice(0,g+1)),S=()=>{let C;return b?C=_:f.route.Component?C=j.createElement(f.route.Component,null):f.route.element?C=f.route.element:C=u,j.createElement(fI,{match:f,routeContext:{outlet:u,matches:O,isDataRoute:r!=null},children:C})};return r&&(f.route.ErrorBoundary||f.route.errorElement||g===0)?j.createElement(dI,{location:r.location,revalidation:r.revalidation,component:_,error:b,children:S(),routeContext:{outlet:null,matches:O,isDataRoute:!0}}):S()},null)}var S5=function(e){return e.UseBlocker="useBlocker",e.UseRevalidator="useRevalidator",e.UseNavigateStable=Line truncated
* React Router DOM v6.17.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/function pd(){return pd=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},pd.apply(this,arguments)}function O5(e,t){if(e==null)return{};var r={},n=Object.keys(e),o,a;for(a=0;a<n.length;a++)o=n[a],!(t.indexOf(o)>=0)&&(r[o]=e[o]);return r}function xI(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function _I(e,t){return e.button===0&&(!t||t==="_self")&&!xI(e)}const CI=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","unstable_viewTransition"],SI=["aria-current","caseSensitive","className","end","style","to","unstable_viewTransition","children"],TI=j.createContext({isTransitioning:!1}),OI="startTransition",gw=Yh[OI];function PI(e){let{basename:t,children:r,future:n,window:o}=e,a=j.useRef();a.current==null&&(a.current=IM({window:o,v5Compat:!0}));let i=a.current,[u,f]=j.useState({action:i.action,location:i.location}),{v7_startTransition:g}=n||{},b=j.useCallback(_=>{g&&gw?gw(()=>f(_)):f(_)},[f,g]);return j.useLayoutEffect(()=>i.listen(b),[i,b]),j.createElement(wI,{basename:t,children:r,location:u.location,navigationType:u.action,navigator:i})}const kI=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",EI=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,P5=j.forwardRef(function(t,r){let{onClick:n,relative:o,reloadDocument:a,replace:i,state:u,target:f,to:g,preventScrollReset:b,unstable_viewTransition:_}=t,O=O5(t,CI),{basename:S}=j.useContext(ai),C,w=!1;if(typeof g=="string"&&EI.test(g)&&(C=g,kI))try{let c=new URL(window.location.href),s=g.startsWith("//")?new URL(c.protocol+g):new URL(g),h=Vs(s.pathname,S);s.origin===c.origin&&h!=null?g=h+s.search+s.hash:w=!0}catch{}let m=aI(g,{relative:o}),d=RI(g,{replace:i,state:u,target:f,preventScrollReset:b,relative:o,unstable_viewTransition:_});function l(c){n&&n(c),c.defaultPrevented||d(c)}return j.createElement("a",pd({},O,{href:C||m,onClick:w||a?n:l,ref:r,target:f}))}),NI=j.forwardRef(function(t,r){let{"aria-current":n="page",caseSensitive:o=!1,className:a="",end:i=!1,style:u,to:f,unstable_viewTransition:g,children:b}=t,_=O5(t,SI),O=nf(f,{relative:_.relative}),S=_a(),C=j.useContext(w5),{navigator:w}=j.useContext(ai),m=C!=null&&AI(O)&&g===!0,d=w.encodeLocation?w.encodeLocation(O).pathname:O.pathname,l=S.pathname,c=C&&C.navigation&&C.navigation.location?C.navigation.location.pathname:null;o||(l=l.toLowerCase(),c=c?c.toLowerCase():null,d=d.toLowerCase());let s=l===d||!i&&l.startsWith(d)&&l.charAt(d.length)==="/",h=c!=null&&(c===d||!i&&c.startsWith(d)&&c.charAt(d.length)==="/"),v={isActive:s,isPending:h,isTransitioning:m},y=s?n:void 0,E;typeof a=="function"?E=a(v):E=[a,s?"active":null,h?"pending":null,m?"transitioning":null].filter(Boolean).join(" ");let P=typeof u=="function"?u(v):u;return j.createElement(P5,pd({},_,{"aria-current":y,className:E,ref:r,style:P,to:f,unstable_viewTransition:g}),typeof b=="function"?b(v):b)});var gh;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(gh||(gh={}));var mw;(function(e){e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(mw||(mw={}));function jI(e){let t=j.useContext(tf);return t||Ht(!1),t}function RI(e,t){let{target:r,replace:n,state:o,preventScrollReset:a,relative:i,unstable_viewTransition:u}=t===void 0?{}:t,f=ii(),g=_a(),b=nf(e,{relative:i});return j.useCallback(_=>{if(_I(_,r)){_.preventDefault();let O=n!==void 0?n:cd(g)===cd(b);f(e,{replace:O,state:o,preventScrollReset:a,relative:i,unstable_viewTransition:u})}},[g,f,b,n,o,r,e,a,i,u])}function AI(e,t){t===void 0&&(t={});let r=j.useContext(TI);r==null&&Ht(!1);let{basename:n}=jI(gh.useViewTransitionState),o=nf(e,{relative:t.relative});if(!r.isTransitioning)return!1;let a=Vs(r.currentLocation.pathname,n)||r.currentLocation.pathname,i=Vs(r.nextLocation.pathname,n)||r.nextLocation.pathname;return ph(o.pathname,i)!=null||ph(o.pathname,a)!=null}function MI({title:e,titleId:t,...r},n){return j.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":"true",ref:n,"aria-labelledby":t},r),e?j.createElement("title",{id:t},e):null,j.createElement("path",{fillRule:"evenodd",d:"M3 6.75A.75.75 0 013.75 6h16.5a.75.75 0 010 1.5H3.75A.75.75 0 013 6.75zM3 12a.75.75 0 01.75-.75h16.5a.75.75 0 010 1.5H3.75A.75.75 0 013 12zm0 5.25a.75.75 0 01.75-.75h16.5a.75.75 0 010 1.5H3.75a.75.75 0 01-.75-.75z",clipRule:"evenodd"}))}const II=j.forwardRef(MI),DI=II;function LI({title:e,titleId:t,...r},n){return j.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":"true",ref:n,"aria-labelledby":t},r),e?j.createElement("title",{id:t},e):null,j.createElement("path",{fillRule:"evenodd",d:"M11.078 2.25c-.917 0-1.69Line truncated
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/(function(e){(function(){var t={}.hasOwnProperty;function r(){for(var n=[],o=0;o<arguments.length;o++){var a=arguments[o];if(a){var i=typeof a;if(i==="string"||i==="number")n.push(a);else if(Array.isArray(a)){if(a.length){var u=r.apply(null,a);u&&n.push(u)}}else if(i==="object"){if(a.toString!==Object.prototype.toString&&!a.toString.toString().includes("[native code]")){n.push(a.toString());continue}for(var f in a)t.call(a,f)&&a[f]&&n.push(f)}}}return n.join(" ")}e.exports?(r.default=r,e.exports=r):window.classNames=r})()})(N5);var Xe=N5.exports,j5={exports:{}},Nn={};function Xg(){for(var e,t,r=0,n="";r<arguments.length;)(e=arguments[r++])&&(t=R5(e))&&(n&&(n+=" "),n+=t);return n}function R5(e){if(typeof e=="string")return e;for(var t,r="",n=0;n<e.length;n++)e[n]&&(t=R5(e[n]))&&(r&&(r+=" "),r+=t);return r}function mh(){return mh=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},mh.apply(this,arguments)}function i4(e){var t=function(r){var n=r.theme,o=r.prefix,a={nextPart:new Map,validators:[]};return function(i,u){return u?i.map(function(f){return[f[0],f[1].map(function(g){return typeof g=="string"?u+g:typeof g=="object"?Object.fromEntries(Object.entries(g).map(function(b){return[u+b[0],b[1]]})):g})]}):i}(Object.entries(r.classGroups),o).forEach(function(i){bh(i[1],a,i[0],n)}),a}(e);return{getClassGroupId:function(r){var n=r.split("-");return n[0]===""&&n.length!==1&&n.shift(),A5(n,t)||function(o){if(bw.test(o)){var a=bw.exec(o)[1],i=a==null?void 0:a.substring(0,a.indexOf(":"));if(i)return"arbitrary.."+i}}(r)},getConflictingClassGroupIds:function(r){return e.conflictingClassGroups[r]||[]}}}function A5(e,t){var r;if(e.length===0)return t.classGroupId;var n=t.nextPart.get(e[0]),o=n?A5(e.slice(1),n):void 0;if(o)return o;if(t.validators.length!==0){var a=e.join("-");return(r=t.validators.find(function(i){return(0,i.validator)(a)}))==null?void 0:r.classGroupId}}Object.defineProperty(Nn,"__esModule",{value:!0});var bw=/^\[(.+)\]$/;function bh(e,t,r,n){e.forEach(function(o){if(typeof o!="string"){if(typeof o=="function")return o.isThemeGetter?void bh(o(n),t,r,n):void t.validators.push({validator:o,classGroupId:r});Object.entries(o).forEach(function(a){bh(a[1],vw(t,a[0]),r,n)})}else(o===""?t:vw(t,o)).classGroupId=r})}function vw(e,t){var r=e;return t.split("-").forEach(function(n){r.nextPart.has(n)||r.nextPart.set(n,{nextPart:new Map,validators:[]}),r=r.nextPart.get(n)}),r}function l4(e){if(e<1)return{get:function(){},set:function(){}};var t=0,r=new Map,n=new Map;function o(a,i){r.set(a,i),++t>e&&(t=0,n=r,r=new Map)}return{get:function(a){var i=r.get(a);return i!==void 0?i:(i=n.get(a))!==void 0?(o(a,i),i):void 0},set:function(a,i){r.has(a)?r.set(a,i):o(a,i)}}}function s4(e){var t=e.separator||":";return function(r){for(var n=0,o=[],a=0,i=0;i<r.length;i++){var u=r[i];n===0&&u===t[0]&&(t.length!==1&&r.slice(i,i+t.length)!==t||(o.push(r.slice(a,i)),a=i+t.length)),u==="["?n++:u==="]"&&n--}var f=o.length===0?r:r.substring(a),g=f.startsWith("!");return{modifiers:o,hasImportantModifier:g,baseClassName:g?f.substring(1):f}}}function u4(e){return mh({cache:l4(e.cacheSize),splitModifiers:s4(e)},i4(e))}var c4=/\s+/;function d4(e,t){var r=t.splitModifiers,n=t.getClassGroupId,o=t.getConflictingClassGroupIds,a=new Set;return e.trim().split(c4).map(function(i){var u=r(i),f=u.modifiers,g=u.hasImportantModifier,b=n(u.baseClassName);if(!b)return{isTailwindClass:!1,originalClassName:i};var _=function(O){if(O.length<=1)return O;var S=[],C=[];return O.forEach(function(w){w[0]==="["?(S.push.apply(S,C.sort().concat([w])),C=[]):C.push(w)}),S.push.apply(S,C.sort()),S}(f).join(":");return{isTailwindClass:!0,modifierId:g?_+"!":_,classGroupId:b,originalClassName:i}}).reverse().filter(function(i){if(!i.isTailwindClass)return!0;var u=i.modifierId,f=i.classGroupId,g=u+f;return!a.has(g)&&(a.add(g),o(f).forEach(function(b){return a.add(u+b)}),!0)}).reverse().map(function(i){return i.originalClassName}).join(" ")}function vh(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];var n,o,a,i=u;function u(g){var b=t[0],_=t.slice(1).reduce(function(O,S){return S(O)},b());return n=u4(_),o=n.cache.get,a=n.cache.set,i=f,f(g)}function f(g){var b=o(g);if(b)return b;var _=d4(g,n);return a(g,_),_}return function(){return i(Xg.apply(null,arguments))}}function Mt(e){var t=function(r){return r[e]||[]};return t.isThemeGetter=!0,t}var Ca=/^\[(.+)\]$/,f4=/^\d+\/\d+$/,p4=new Set(["px","full","screen"]),h4=/^(\d+)?(xs|sm|md|lg|xl)$/,g4=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh)/,m4=/^-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/;function gn(e){return!Number.isNaN(Number(e))||p4.has(e)||f4.test(e)||Zo(e)}function Zo(e){var t,r=(t=Ca.exec(e))==null?void 0:t[1];return!!r&&(r.startsWith("length:")||g4.test(r))}function M5(e){var t,r=(t=Ca.exec(e))==null?void 0:t[1];return!!r&&r.startsWLine truncated
* tabbable 6.2.0
* @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE
*/var iF=["input:not([inert])","select:not([inert])","textarea:not([inert])","a[href]:not([inert])","button:not([inert])","[tabindex]:not(slot):not([inert])","audio[controls]:not([inert])","video[controls]:not([inert])",'[contenteditable]:not([contenteditable="false"]):not([inert])',"details>summary:first-of-type:not([inert])","details:not([inert])"],Ah=iF.join(","),VO=typeof Element>"u",Gs=VO?function(){}:Element.prototype.matches||Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector,Cd=!VO&&Element.prototype.getRootNode?function(e){var t;return e==null||(t=e.getRootNode)===null||t===void 0?void 0:t.call(e)}:function(e){return e==null?void 0:e.ownerDocument},Sd=function e(t,r){var n;r===void 0&&(r=!0);var o=t==null||(n=t.getAttribute)===null||n===void 0?void 0:n.call(t,"inert"),a=o===""||o==="true",i=a||r&&t&&e(t.parentNode);return i},lF=function(t){var r,n=t==null||(r=t.getAttribute)===null||r===void 0?void 0:r.call(t,"contenteditable");return n===""||n==="true"},sF=function(t,r,n){if(Sd(t))return[];var o=Array.prototype.slice.apply(t.querySelectorAll(Ah));return r&&Gs.call(t,Ah)&&o.unshift(t),o=o.filter(n),o},uF=function e(t,r,n){for(var o=[],a=Array.from(t);a.length;){var i=a.shift();if(!Sd(i,!1))if(i.tagName==="SLOT"){var u=i.assignedElements(),f=u.length?u:i.children,g=e(f,!0,n);n.flatten?o.push.apply(o,g):o.push({scopeParent:i,candidates:g})}else{var b=Gs.call(i,Ah);b&&n.filter(i)&&(r||!t.includes(i))&&o.push(i);var _=i.shadowRoot||typeof n.getShadowRoot=="function"&&n.getShadowRoot(i),O=!Sd(_,!1)&&(!n.shadowRootFilter||n.shadowRootFilter(i));if(_&&O){var S=e(_===!0?i.children:_.children,!0,n);n.flatten?o.push.apply(o,S):o.push({scopeParent:i,candidates:S})}else a.unshift.apply(a,i.children)}}return o},UO=function(t){return!isNaN(parseInt(t.getAttribute("tabindex"),10))},WO=function(t){if(!t)throw new Error("No node provided");return t.tabIndex<0&&(/^(AUDIO|VIDEO|DETAILS)$/.test(t.tagName)||lF(t))&&!UO(t)?0:t.tabIndex},cF=function(t,r){var n=WO(t);return n<0&&r&&!UO(t)?0:n},dF=function(t,r){return t.tabIndex===r.tabIndex?t.documentOrder-r.documentOrder:t.tabIndex-r.tabIndex},HO=function(t){return t.tagName==="INPUT"},fF=function(t){return HO(t)&&t.type==="hidden"},pF=function(t){var r=t.tagName==="DETAILS"&&Array.prototype.slice.apply(t.children).some(function(n){return n.tagName==="SUMMARY"});return r},hF=function(t,r){for(var n=0;n<t.length;n++)if(t[n].checked&&t[n].form===r)return t[n]},gF=function(t){if(!t.name)return!0;var r=t.form||Cd(t),n=function(u){return r.querySelectorAll('input[type="radio"][name="'+u+'"]')},o;if(typeof window<"u"&&typeof window.CSS<"u"&&typeof window.CSS.escape=="function")o=n(window.CSS.escape(t.name));else try{o=n(t.name)}catch(i){return console.error("Looks like you have a radio button with a name attribute containing invalid CSS selector characters and need the CSS.escape polyfill: %s",i.message),!1}var a=hF(o,t.form);return!a||a===t},mF=function(t){return HO(t)&&t.type==="radio"},bF=function(t){return mF(t)&&!gF(t)},vF=function(t){var r,n=t&&Cd(t),o=(r=n)===null||r===void 0?void 0:r.host,a=!1;if(n&&n!==t){var i,u,f;for(a=!!((i=o)!==null&&i!==void 0&&(u=i.ownerDocument)!==null&&u!==void 0&&u.contains(o)||t!=null&&(f=t.ownerDocument)!==null&&f!==void 0&&f.contains(t));!a&&o;){var g,b,_;n=Cd(o),o=(g=n)===null||g===void 0?void 0:g.host,a=!!((b=o)!==null&&b!==void 0&&(_=b.ownerDocument)!==null&&_!==void 0&&_.contains(o))}}return a},qw=function(t){var r=t.getBoundingClientRect(),n=r.width,o=r.height;return n===0&&o===0},yF=function(t,r){var n=r.displayCheck,o=r.getShadowRoot;if(getComputedStyle(t).visibility==="hidden")return!0;var a=Gs.call(t,"details>summary:first-of-type"),i=a?t.parentElement:t;if(Gs.call(i,"details:not([open]) *"))return!0;if(!n||n==="full"||n==="legacy-full"){if(typeof o=="function"){for(var u=t;t;){var f=t.parentElement,g=Cd(t);if(f&&!f.shadowRoot&&o(f)===!0)return qw(t);t.assignedSlot?t=t.assignedSlot:!f&&g!==t.ownerDocument?t=g.host:t=f}t=u}if(vF(t))return!t.getClientRects().length;if(n!=="legacy-full")return!0}else if(n==="non-zero-area")return qw(t);return!1},wF=function(t){if(/^(INPUT|BUTTON|SELECT|TEXTAREA)$/.test(t.tagName))for(var r=t.parentElement;r;){if(r.tagName==="FIELDSET"&&r.disabled){for(var n=0;n<r.children.length;n++){var o=r.children.item(n);if(o.tagName==="LEGEND")return Gs.call(r,"fieldset[disabled] *")?!0:!o.contains(t)}return!0}r=r.parentElement}return!1},xF=function(t,r){return!(r.disabled||Sd(r)||fF(r)||yF(r,t)||pF(r)||wF(r))},Gw=function(t,r){return!(bF(r)||WO(r)<0||!xF(t,r))},_F=function(t){var r=parseInt(t.getAttribute("tabindex"),10);return!!(isNaN(r)||r>=0)},CF=function e(t){var r=[],n=[];return t.forEach(function(o,a){var i=!!o.scopeParent,u=i?o.scopeParent:o,f=cF(u,i),g=i?e(o.candidates):u;f===0?i?r.push.apply(r,g):r.push(u):n.push({documentOrder:a,tabIndex:f,item:o,isScope:i,content:g})}),n.sort(dF).reduce(function(o,a){return a.isScope?o.push.apply(o,a.content):o.push(a.content),o},[]).concatLine truncated
More details: https://www.material-tailwind.com/docs/react/collapse
`);var P=r.default.useRef(null),N=(0,g.useTheme)().navbar,R=N.styles,M=R.base.mobileNav;s=s??{},h=h??"";var D=(0,u.twMerge)((0,i.default)((0,f.default)(M)),h),L={unmount:{height:0,opacity:0,transition:{duration:.3,times:"[0.4, 0, 0.2, 1]"}},mount:{opacity:1,height:"".concat((E=P.current)===null||E===void 0?void 0:E.scrollHeight,"px"),transition:{duration:.3,times:"[0.4, 0, 0.2, 1]"}}},W=(0,a.default)(L,s),U=n.AnimatePresence,Y=(0,o.useMergeRefs)([l,P]);return r.default.createElement(n.LazyMotion,{features:n.domAnimation},r.default.createElement(U,null,r.default.createElement(n.m.div,_({},y,{ref:Y,className:D,initial:"unmount",exit:"unmount",animate:c?"mount":"unmount",variants:W}),v)))});w.displayName="MaterialTailwind.MobileNav",w.propTypes={open:b.propTypesOpen,animate:b.propTypesAnimate,className:b.propTypesClassName,children:b.propTypesChildren};var m=w})(AP);(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(l,c){for(var s in c)Object.defineProperty(l,s,{enumerable:!0,get:c[s]})}t(e,{Navbar:function(){return m},MobileNav:function(){return g.MobileNav},default:function(){return d}});var r=S(j),n=S($e),o=S(Xe),a=He,i=S(zt),u=S(Be),f=ze,g=AP,b=of;function _(l,c,s){return c in l?Object.defineProperty(l,c,{value:s,enumerable:!0,configurable:!0,writable:!0}):l[c]=s,l}function O(){return O=Object.assign||function(l){for(var c=1;c<arguments.length;c++){var s=arguments[c];for(var h in s)Object.prototype.hasOwnProperty.call(s,h)&&(l[h]=s[h])}return l},O.apply(this,arguments)}function S(l){return l&&l.__esModule?l:{default:l}}function C(l,c){if(l==null)return{};var s=w(l,c),h,v;if(Object.getOwnPropertySymbols){var y=Object.getOwnPropertySymbols(l);for(v=0;v<y.length;v++)h=y[v],!(c.indexOf(h)>=0)&&Object.prototype.propertyIsEnumerable.call(l,h)&&(s[h]=l[h])}return s}function w(l,c){if(l==null)return{};var s={},h=Object.keys(l),v,y;for(y=0;y<h.length;y++)v=h[y],!(c.indexOf(v)>=0)&&(s[v]=l[v]);return s}var m=r.default.forwardRef(function(l,c){var s=l.variant,h=l.color,v=l.shadow,y=l.blurred,E=l.fullWidth,P=l.className,N=l.children,R=C(l,["variant","color","shadow","blurred","fullWidth","className","children"]),M=(0,f.useTheme)().navbar,D=M.defaultProps,L=M.valid,W=M.styles,U=W.base,Y=W.variants;s=s??D.variant,h=h??D.color,v=v??D.shadow,y=y??D.blurred,E=E??D.fullWidth,P=P??D.className;var $,z=(0,o.default)((0,u.default)(U.navbar.initial),($={},_($,(0,u.default)(U.navbar.shadow),v),_($,(0,u.default)(U.navbar.blurred),y&&h==="white"),_($,(0,u.default)(U.navbar.fullWidth),E),$)),H=(0,o.default)((0,u.default)(Y[(0,i.default)(L.variants,s,"filled")][(0,i.default)(L.colors,h,"white")])),G=(0,a.twMerge)((0,o.default)(z,H),P);return r.default.createElement("nav",O({},R,{ref:c,className:G}),N)});m.propTypes={variant:n.default.oneOf(b.propTypesVariant),color:n.default.oneOf(b.propTypesColor),shadow:b.propTypesShadow,blurred:b.propTypesBlurred,fullWidth:b.propTypesFullWidth,className:b.propTypesClassName,children:b.propTypesChildren},m.displayName="MaterialTailwind.Navbar";var d=Object.assign(m,{MobileNav:g.MobileNav})})(RP);var MP={},Of={},Ol={};(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(m,d){for(var l in d)Object.defineProperty(m,l,{enumerable:!0,get:d[l]})}t(e,{propTypesOpen:function(){return a},propTypesHandler:function(){return i},propTypesPlacement:function(){return u},propTypesOffset:function(){return f},propTypesDismiss:function(){return g},propTypesAnimate:function(){return b},propTypesContent:function(){return _},propTypesInteractive:function(){return O},propTypesClassName:function(){return S},propTypesChildren:function(){return C},propTypesContextValue:function(){return w}});var r=o($e),n=Dt;function o(m){return m&&m.__esModule?m:{default:m}}var a=r.default.bool,i=r.default.func,u=n.propTypesPlacements,f=n.propTypesOffsetType,g=n.propTypesDismissType,b=n.propTypesAnimation,_=r.default.node,O=r.default.bool,S=r.default.string,C=r.default.node.isRequired,w=r.default.shape({open:r.default.bool.isRequired,strategy:r.default.oneOf(["fixed","absolute"]).isRequired,x:r.default.number,y:r.default.number,context:r.default.instanceOf(Object).isRequired,reference:r.default.func.isRequired,floating:r.default.func.isRequired,getReferenceProps:r.default.func.isRequired,getFloatingProps:r.default.func.isRequired,appliedAnimation:b.isRequired,labelId:r.default.string.isRequired,descriptionId:r.default.string.isRequired}).isRequired})(Ol);(function(e){Object.defineProperty(e,"__esModule",{value:!0});function t(f,g){for(var b in g)Object.defineProperty(f,b,{enumerable:!0,get:g[b]})}t(e,{PopoverContext:function(){return a},usePopover:function(){return i},PopoverContextProvider:function(){return u}});var r=o(j),n=Ol;function o(f){return f&&f.__esModule?f:{default:f}}var a=r.default.createContext(null);a.displayName="MaterialTailwind.PopoverContext";function i(){var f=r.default.useContext(a);if(!f)throw new Error("usePopover() must be used within a Popover. It happens when you us Line truncated
${we?"border-purple-500 ring-1 ring-purple-500/40":"border-transparent"}
${!Ze||Ce?"opacity-75 bg-stormGrey-200/20":""}`,children:[T.jsxs("div",{className:"flex items-center space-x-4",id:"left",children:[T.jsx("input",{type:"checkbox",checked:we,onChange:()=>Te(te.Id,!Ze),className:`h-5 w-5 text-purple-500 focus:ring-purple-400 border-gray-600 bg-gray-900 rounded ${Ze?"":"opacity-40 cursor-not-allowed"}`,disabled:!Ze}),T.jsx("div",{className:`w-12 h-12 flex items-center justify-center rounded-full
${te.updateStatus==="✅"?"bg-mossGreen-600":te.updateStatus==="⚠️"?"bg-warmAmberGlow-400":"bg-sunsetCoral-500"}`}),T.jsxs("div",{children:[T.jsx("p",{className:"text-lg antialiased font-sans font-semibold leading-normal text-inherit",children:te.Name}),T.jsxs("p",{className:"text-sm antialiased font-sans font-light leading-normal text-inherit",children:["ID: ",te.Id]}),te.duplicateName&&T.jsx("p",{className:"text-xs text-amber-700",children:"⚠️ Doppelter Name erkannt"})]})]}),T.jsx("div",{className:"ml-auto flex flex-col items-end gap-1 text-sm",id:"right",children:Pe?T.jsxs(T.Fragment,{children:[T.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-warmAmberGlow-600",children:"Redeploy"}),T.jsx("span",{className:"text-warmAmberGlow-500",children:"läuft…"})]}):_e?T.jsxs(T.Fragment,{children:[T.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-warmAmberGlow-600",children:"Redeploy"}),T.jsx("span",{className:"text-warmAmberGlow-500",children:"in Warteliste…"})]}):Ue?T.jsxs(T.Fragment,{children:[T.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-stormGrey-500",children:"System"}),T.jsx("span",{className:"text-gray-300",children:"Redeploy deaktiviert"})]}):Ie?T.jsxs(T.Fragment,{children:[T.jsx("span",{className:"antialiased font-sans font-light leading-normal text-xs uppercase tracking-wide text-stormGrey-500",children:"Status"}),T.jsx("span",{className:"text-mossGreen-600",children:"Aktuell"})]}):T.jsx(Q.Button,{onClick:()=>qe(te.Id),disabled:Ce,className:"disabled:opacity-50 disabled:cursor-not-allowed",children:"Redeploy"})})]})},te.Id)})}),T.jsx(jf,{disabled:r||!!o})]})}const NB={idle:"Bereit",running:"Läuft",success:"Erfolgreich",error:"Fehlgeschlagen"},es={idle:"bg-stormGrey-700 text-gray-200",running:"bg-arcticBlue-600 text-white",success:"bg-mossGreen-600 text-white",error:"bg-sunsetCoral-600 text-white"},jB={initializing:"Vorbereitung","activating-maintenance":"Wartungsmodus aktivieren","executing-script":"Skript wird ausgeführt",waiting:"Warte auf Portainer",completed:"Abgeschlossen",failed:"Fehlgeschlagen"},RB={info:"text-arcticBlue-700",success:"text-mossGreen-700",warning:"text-warmAmberGlow-700",error:"text-sunsetCoral-700",stdout:"text-stormGrey-700",stderr:"text-warmAmberGlow-700",debug:"text-lavenderSmoke-700"},AB=e=>{if(!e)return"-";const t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleString("de-DE",{hour:"2-digit",minute:"2-digit",second:"2-digit"})},Ma=e=>{if(!e&&e!==0)return"-";const r=(n=>{if(n instanceof Date)return n;if(typeof n=="number"){const o=n>1e12?n:n*1e3;return new Date(o)}if(typeof n=="string"){const o=Number(n);if(!Number.isNaN(o)){const a=o>1e12?o:o*1e3;return new Date(a)}return new Date(n)}return null})(e);return!r||Number.isNaN(r.getTime())?String(e):r.toLocaleString("de-DE",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"})},kx=e=>e===1?"Git":e===2?"Compose":e??"-",c0=()=>({host:"",port:"22",username:"",password:"",extraSshArgs:""});function MB(){var Xn,Ea,Rl;const{showToast:e}=El(),{maintenance:t,update:r,script:n,ssh:o,loading:a,error:i,setMaintenanceMode:u,triggerUpdate:f,saveScript:g,resetScript:b,saveSshConfig:_,deleteSshConfig:O,testSshConnection:S,fetchSuperuserStatus:C,fetchSetupStatus:w,deleteSetupEndpoint:m,deleteSetupServer:d,updateSetupApiKey:l,removeSuperuserAccount:c}=pi(),s=!!(t!=null&&t.active),h=t==null?void 0:t.message,v=(Xn=t==null?void 0:t.extra)==null?void 0:Xn.type,y=!!(r!=null&&r.running),[E,P]=j.useState(""),[N,R]=j.useState(!1),[M,D]=j.useState(!1),[L,W]=j.useState(""),[U,Y]=j.useState(!1),[$,z]=j.useState(()=>c0()),[H,G]=j.useState(!1),[X,ie]=j.useState(!1),[fe,pe]=j.useState(!1),[oe,se]=j.useState(!1),[ee,ae]=j.useState(!1),[ue,de]=j.useState(null);j.useEffect(()=>{if(!n)return;const he=n.custom??n.default??n.effective??"";P(he)},[n]),j.useEffect(()=>{if(!o){z(c0()),G(!1),ie(!1),de(null);return}z({host:o.host??"",port:String(o.port??"22"),username:o.username??"",password:"",extraSshArgs:Array.isArray(o.extraSshArgs)?o.extraSshArgs.join(`
`):""}),G(!!o.passwordStored),ie(!1),de(null)},[o]);const Te=j.useMemo(()=>n?n.source==="custom"&&typeof n.custom=="string"?n.custom:n.default??n.effective??"":"",[n]),xe=n?E!==Te:!1,be=(n==null?void 0:n.source)==="custom"?"Benutzerdefiniert":"Standard",[Se,je]=j.useState(!0),[qe,nt]=j.useState(!1),[ut,Ye]=j.useState(null),[rt,Je]=j.useState(""),[Ve,me]=j.useState(!1),[te,ge]=j.useState(null),[_e,Pe]=j.useState(!0),[Ce,we]=j.useState(""),[Ie,Ue]=j.useState({}),[Ze,Bt]=j.useState(null),[et,Xt]=j.useState(null),[qt,Et]=j.useState(null),[tr,Jt]=j.useState([]),[Nt,Nr]=j.useState(!0),[bt,Gt]=j.useState(""),[On,Gn]=j.useState(!1),[fo,Kr]=j.useState(null),[Kn,wr]=j.useState(null),Yr=j.useRef(null),[pt,po]=j.useState(null),[Z,Oe]=j.useState(!0),[Re,_t]=j.useState(""),[jt,vt]=j.useState(!1),[Ct,cr]=j.useState(null),lt=j.useRef(null),xr=j.useCallback(async({silent:he=!1}={})=>{var Ne,Ae;je(!0),Je("");try{const Me=await C(),Ke=!!(Me!=null&&Me.exists);nt(Ke),Ye(Ke?Me.user??null:null)}catch(Me){const Ke=((Ae=(Ne=Me.response)==null?void 0:Ne.data)==null?void 0:Ae.error)||Me.message||"Superuser-Status konnte nicht geladen werden";nt(!1),Ye(null),Je(Ke),he||e({variant:"error",title:"Statusabfrage fehlgeschlagen",description:Ke})}finally{je(!1)}},[C,e]),Zt=j.useCallback(async({silent:he=!1}={})=>{var Ne,Ae;he||Pe(!0),we("");try{const Me=await w();ge(Me),Ue({})}catch(Me){const Ke=((Ae=(Ne=Me.response)==null?void 0:Ne.data)==null?void 0:Ae.error)||Me.message||"Setup-Daten konnten nicht geladen werden";we(Ke),he||e({variant:"error",title:"Setup-Daten",description:Ke})}finally{Pe(!1)}},[w,e]),ar=j.useCallback(async({silent:he=!1}={})=>{if(lt.current)return lt.current;const Ne=(async()=>{var Ae,Me;he?vt(!0):Oe(!0),_t("");try{const Tt=(await at.get("/api/maintenance/portainer-status")).data??{};po(Tt),cr(new Date)}catch(Ke){const Tt=((Me=(Ae=Ke.response)==null?void 0:Ae.data)==null?void 0:Me.error)||Ke.message||"Fehler beim Prüfen des Portainer-Status";_t(Tt),e({variant:"error",title:"Statusaktualisierung fehlgeschlagen",description:Tt})}finally{he?vt(!1):Oe(!1),lt.current=null}})();return lt.current=Ne,Ne},[e]),Fr=j.useCallback(async({silent:he=!1}={})=>{if(s||y){Jt([]),Gt("Wartungsmodus aktiv – Duplikat-Verwaltung ist vorübergehend deaktiviert."),Nr(!1),Gn(!1);return}if(Yr.current)return Yr.current;const Ne=(async()=>{var Ae,Me,Ke;he?Gn(!0):Nr(!0),Gt("");try{const wt=(await at.get("/api/maintenance/duplicates")).data,Yt=Array.isArray(wt)?wt:(wt==null?void 0:wt.items)??[];Jt(Yt),wr(new Date)}catch(Tt){if(((Ae=Tt.response)==null?void 0:Ae.status)===423){const wt="Wartungsmodus aktiv – Duplikat-Verwaltung ist vorübergehend deaktiviert.";Jt([]),Gt(wt),e({variant:"warning",title:"Duplikat-Verwaltung gesperrt",description:wt})}else{const wt=((Ke=(Me=Tt.response)==null?void 0:Me.data)==null?void 0:Ke.error)||Tt.message||"Fehler beim Laden der Wartungsdaten";Gt(wt),e({variant:"error",title:"Duplikate konnten nicht geladen werden",description:wt})}}finally{he?Gn(!1):Nr(!1),Yr.current=null}})();return Yr.current=Ne,Ne},[s,y,e]);j.useEffect(()=>{xr({silent:!0})},[xr]),j.useEffect(()=>{Zt()},[Zt]),j.useEffect(()=>{ar()},[ar]),j.useEffect(()=>{Fr()},[Fr]);const Pn=j.useMemo(()=>{var he;return((he=te==null?void 0:te.servers)==null?void 0:he.items)??[]},[te]),Qr=j.useMemo(()=>{var he;return((he=te==null?void 0:te.endpoints)==null?void 0:he.items)??[]},[te]),Sa=j.useMemo(()=>{var Ne;const he=((Ne=te==null?void 0:te.apiKeys)==null?void 0:Ne.items)??[];return new Map(he.map(Ae=>[Ae.serverId,Ae]))},[te]),Ta=j.useMemo(()=>!!(te!=null&&te.setupComplete),[te]),Yn=j.useMemo(()=>{const he=Array.isArray(tr)?tr.length:0,Ne=Array.isArray(tr)?tr.reduce((Ae,Me)=>{var Ke;return Ae+(((Ke=Me==null?void 0:Me.duplicates)==null?void 0:Ke.length)||0)},0):0;return{groups:he,duplicateCount:Ne}},[tr]),Xr=j.useCallback((he,Ne)=>{z(Ae=>({...Ae,[he]:Ne})),he==="password"&&G(!1)},[]),Qn=j.useMemo(()=>{const he={host:$.host.trim(),port:Number.parseInt($.port,10)||22,username:$.username.trim(),extraSshArgs:($.extraSshArgs||"").split(/\r?\n/).map(Ae=>Ae.trim()).filter(Boolean)},Ne=$.password??"";return Ne?he.password=Ne:H||(he.password=""),he},[$,H]),hi=j.useCallback(async()=>{var he,Ne;if(n)try{R(!0),await g(E),e({variant:"success",title:"Skript gespeichert",description:"Das benutzerdefinierte Portainer-Update-Skript wurde aktualisiert."})}catch(Ae){const Me=((Ne=(he=Ae.response)==null?void 0:he.data)==null?void 0:Ne.error)||Ae.message||"Skript konnte nicht gespeichert werden";e({variant:"error",title:"Speichern fehlgeschlagen",description:Me})}finally{R(!1)}},[g,E,n,e]),ho=j.useCallback(async()=>{var he,Ne;try{R(!0),await b(),e({variant:"info",title:"Standardskript wiederhergestellt",description:"Es wird wieder das Standard-Update-Skript verwendet."})}catch(Ae){const Me=((Ne=(he=Ae.response)==null?void 0:he.data)==null?void 0:Ne.error)||Ae.message||"Standardskript konnte nicht wiederhergestellt werden";e({variant:"error",title:"Zurücksetzen fehlgeschlagen",descr Line truncated
Dies setzt die Superuser-Einrichtung zurück.`)))try{me(!0);const Me=await c(),Ke=Number((Me==null?void 0:Me.usersRemoved)??0);e({variant:"success",title:"Superuser gelöscht",description:Ke>1?`${Ke} Konten aus der Superuser-Gruppe wurden entfernt.`:"Superuser-Konto wurde entfernt."}),await xr({silent:!0})}catch(Me){if(((he=Me.response)==null?void 0:he.status)===404)await xr({silent:!0}),e({variant:"info",title:"Kein Superuser vorhanden",description:"Es ist kein Superuser mehr vorhanden."});else{const Tt=((Ae=(Ne=Me.response)==null?void 0:Ne.data)==null?void 0:Ae.error)||Me.message||"Superuser konnte nicht gelöscht werden";e({variant:"error",title:"Löschen fehlgeschlagen",description:Tt})}}finally{me(!1)}},[Ve,Se,qe,c,xr,e]),jl=j.useCallback(async he=>{var Ke,Tt;if(qt===he)return;const Ne=Qr.find(wt=>wt.id===he);if(!Ne)return;const Ae=Ne.name||`Endpoint ${Ne.external_id}`,Me=`Endpoint "${Ae}" wirklich löschen?${Ne.is_default?`
Dieser Endpoint ist aktuell als Standard markiert.`:""}`;if(!(typeof window<"u"&&!window.confirm(Me))){Et(he);try{await m(he),e({variant:"success",title:"Endpoint gelöscht",description:`Endpoint "${Ae}" wurde entfernt.`}),await Zt({silent:!0})}catch(wt){const Yt=((Tt=(Ke=wt.response)==null?void 0:Ke.data)==null?void 0:Tt.error)||wt.message||"Endpoint konnte nicht gelöscht werden.";e({variant:"error",title:"Endpoint löschen fehlgeschlagen",description:Yt})}finally{Et(null)}}},[m,qt,Zt,Qr,e]),Pa=j.useCallback(async he=>{var Tt,wt;if(et===he)return;const Ne=Pn.find(Yt=>Yt.id===he);if(!Ne)return;const Ae=Qr.filter(Yt=>Yt.server_id===he),Me=Ne.name||Ne.url||`Server ${he}`,Ke=[`Server "${Me}" wirklich löschen?`,Ae.length?`Dabei werden ${Ae.length} zugeordnete Endpoint(s) entfernt.`:null,"Das System benötigt anschließend erneut einen gültigen Server/Endpoint im Setup."].filter(Boolean).join(`
`);if(!(typeof window<"u"&&!window.confirm(Ke))){Xt(he);try{await d(he),e({variant:"success",title:"Server gelöscht",description:`Server "${Me}" wurde entfernt.`}),await Zt({silent:!0})}catch(Yt){const _r=((wt=(Tt=Yt.response)==null?void 0:Tt.data)==null?void 0:wt.error)||Yt.message||"Server konnte nicht gelöscht werden.";e({variant:"error",title:"Server löschen fehlgeschlagen",description:_r})}finally{Xt(null)}}},[d,Zt,et,Qr,Pn,e]),go=j.useCallback((he,Ne)=>{Ue(Ae=>({...Ae,[he]:Ne}))},[]),Pt=j.useCallback(async he=>{var Ae,Me;const Ne=(Ie[he]??"").trim();if(!Ne){e({variant:"warning",title:"API-Key fehlt",description:"Bitte gib einen API-Key ein."});return}if(Ze!==he){Bt(he);try{await l(he,Ne),e({variant:"success",title:"API-Key aktualisiert",description:"Der API-Key wurde gespeichert."}),Ue(Ke=>({...Ke,[he]:""})),await Zt({silent:!0})}catch(Ke){const Tt=((Me=(Ae=Ke.response)==null?void 0:Ae.data)==null?void 0:Me.error)||Ke.message||"API-Key konnte nicht aktualisiert werden.";e({variant:"error",title:"Aktualisierung fehlgeschlagen",description:Tt})}finally{Bt(null)}}},[Ie,Ze,Zt,e,l]),Kt=j.useCallback(async he=>{var Ne,Ae;if(!(a||U)&&he!==s){if(y){e({variant:"warning",title:"Aktion nicht möglich",description:"Während eines laufenden Updates kann der Wartungsmodus nicht geändert werden."});return}Y(!0);try{const Me=he&&!!v,Ke={active:he};Me&&h?Ke.message=h:Ke.message=null,await u(Ke),e({variant:he?"info":"success",title:he?"Wartungsmodus aktiviert":"Wartungsmodus deaktiviert",description:he?"Benutzer sehen jetzt die Wartungsseite.":"StackPulse steht wieder zur Verfügung."})}catch(Me){const Ke=((Ae=(Ne=Me.response)==null?void 0:Ne.data)==null?void 0:Ae.error)||Me.message||"Wartungsmodus konnte nicht geändert werden";e({variant:"error",title:"Änderung fehlgeschlagen",description:Ke})}finally{Y(!1)}}},[a,U,s,y,u,h,v,e]),Rt=j.useCallback(async()=>{var Ne,Ae;if(s||y){e({variant:"warning",title:"Update nicht möglich",description:"Während eines aktiven Wartungsmodus kann kein weiteres Update gestartet werden."});return}const he=(pt==null?void 0:pt.latestVersion)??(pt==null?void 0:pt.currentVersion)??"unbekannt";if(!(typeof window<"u"&&!window.confirm(`Portainer-Update starten?
Zielversion: ${he}.
Während des Updates befindet sich StackPulse im Wartungsmodus.
Fortfahren?`)))try{W(""),D(!0),await f(),e({variant:"info",title:"Update gestartet",description:"Das Portainer-Update wurde gestartet."})}catch(Me){const Ke=((Ae=(Ne=Me.response)==null?void 0:Ne.data)==null?void 0:Ae.error)||Me.message||"Portainer-Update konnte nicht gestartet werden";W(Ke),e({variant:"error",title:"Update fehlgeschlagen",description:Ke})}finally{D(!1)}},[s,y,pt,f,e]),cn=j.useCallback(async he=>{var Ke,Tt,wt,Yt;if(!he)return;if(s||y){e({variant:"warning",title:"Aktion nicht möglich",description:"Während des Wartungsmodus sind Bereinigungen deaktiviert."});return}const Ne=(Ke=he.canonical)==null?void 0:Ke.Id;if(!Ne)return;const Ae=(he.duplicates||[]).map(_r=>_r.Id).filter(Boolean);if(!Ae.length)return;const Me=((Tt=he.canonical)==null?void 0:Tt.Name)||he.name||`Stack ${Ne}`;if(!(typeof window<"u"&&!window.confirm(`Bereinigung für "${Me}" starten?
Es werden ${Ae.length} Duplikate entfernt: ${Ae.join(", ")}`))){Kr(String(Ne));try{const mo=(await at.post("/api/maintenance/duplicates/cleanup",{canonicalId:Ne,duplicateIds:Ae})).data??{};if(mo.success===!1)throw new Error(mo.error||"Bereinigung fehlgeschlagen");const Al=Array.isArray(mo.results)?mo.results.filter(Na=>Na.status==="deleted").map(Na=>Na.id):Ae;e({variant:"success",title:"Bereinigung abgeschlossen",description:`${Me} – entfernte IDs: ${Al.join(", ")}`}),await Fr({silent:!0})}catch(_r){const mo=((Yt=(wt=_r.response)==null?void 0:wt.data)==null?void 0:Yt.error)||_r.message||"Bereinigung fehlgeschlagen";e({variant:"error",title:"Bereinigung fehlgeschlagen",description:mo})}finally{Kr(null)}}},[Fr,s,e,y]);t!=null&&t.activatedAt&&Ma(t.activatedAt),t!=null&&t.updatedAt&&Ma(t.updatedAt);const jr=s||y,dn=(r==null?void 0:r.status)??(y?"running":"idle"),ka=NB[dn]??dn,gi=es[dn]??es.idle,mi=r!=null&&r.stage?jB[r.stage]??r.stage:"–",bi=(r==null?void 0:r.logs)??[],Rf=(r==null?void 0:r.targetVersion)??"-",Af=(r==null?void 0:r.resultVersion)??"-",Mf=r!=null&&r.startedAt?Ma(r.startedAt):"-",If=r!=null&&r.finishedAt?Ma(r.finishedAt):"-",vu=(r==null?void 0:r.message)??(y?"Update läuft…":""),yu=y||N||a;return T.jsxs("div",{className:"mt-12 mb-8 flex flex-col gap-12",children:[(s||y)&&T.jsx("div",{className:"rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100",children:T.jsxs("div",{className:"flex flex-col gap-1",children:[T.jsxs("span",{children:["Wartungsmodus aktiv",h?` – ${h}`:y?" – Portainer-Update läuft":"","."]}),y&&T.jsxs("span",{className:"text-xs text-indigo-900",children:["Phase: ",mi]})]})}),T.jsxs(Q.Card,{children:[T.jsx(Q.CardHeader,{variant:"gradient",color:"gray",className:"mb-5 p-4",children:T.jsx(Q.Typography,{variant:"h6",color:"white",className:"flex items-center justify-between",children:T.jsx("span",{children:"Wartungsmodus"})})}),T.jsxs(Q.CardBody,{className:"flex flex-col gap-4 p-4",children:[T.jsxs("div",{className:"flex items-start justify-between gap-4",children:[T.jsx("div",{className:"space-y-1",children:T.jsx("p",{className:"text-sm antialiased font-sans text-sm font-light leading-normal text-inherit",children:"Schaltet StackPulse für Benutzer in den Wartungsmodus."})}),T.jsx(Q.Switch,{checked:s,disabled:U||a||y,onChange:he=>Kt(he.target.checked),ripple:!1,color:"amber"})]}),U&&T.jsx("p",{className:"text-xs text-stormGrey-500",children:"Wartungsmodus wird aktualisiert…"})]})]}),T.jsxs(Q.Card,{children:[T.jsx(Q.CardHeader,{variant:"gradient",color:"gray",className:"mb-5 p-4",children:T.jsx(Q.Typography,{variant:"h6",color:"white",className:"flex items-center justify-between",children:T.jsx("span",{children:"Server & Endpoints"})})}),T.jsxs(Q.CardBody,{className:"flex flex-col gap-4 p-4",children:[_e&&T.jsx("p",{className:"text-sm text-stormGrey-500",children:"Daten werden geladen…"}),!_e&&Ce&&T.jsx(Alert,{color:"red",className:"border border-red-200 bg-red-50 text-red-700",children:Ce}),!_e&&!Ce&&T.jsxs("div",{className:"flex flex-col gap-6",children:[T.jsxs("div",{className:"flex flex-col gap-3",children:[T.jsxs("div",{className:"flex items-center justify-between",children:[T.jsx(Q.Typography,{variant:"small",color:"blue-gray",className:"font-semibold uppercase",children:"Server"}),T.jsxs(Q.Typography,{variant:"small",color:"blue-gray",children:[Pn.length," vorhanden"]})]}),Pn.length===0?T.jsx("p",{className:"text-sm text-stormGrey-500",children:"Es sind derzeit keine Server hinterlegt."}):T.jsx("div",{className:"flex flex-col gap-3",children:Pn.map(he=>{const Ne=Sa.get(he.id)||null,Ae=!!(Ne!=null&&Ne.hasKey),Me=Ne!=null&&Ne.updatedAt?new Date(Ne.updatedAt).toLocaleString("de-DE",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}):null;return T.jsxs("div",{className:"flex flex-col gap-3 rounded-md border border-blue-gray-100 p-3 md:flex-row md:items-start md:justify-between",children:[T.jsxs("div",{className:"flex flex-col gap-1",children:[T.jsx("p",{className:"text-sm font-medium text-stormGrey-900",children:he.name||"Ohne Namen"}),T.jsx("p",{className:"text-xs text-stormGrey-500 break-all",children:he.url}),T.jsxs("p",{className:"text-xs text-stormGrey-500",children:["API-Key: ",Ae?"vorhanden":"nicht hinterlegt",Me?` (aktualisiert am ${Me})`:""]})]}),T.jsxs("div",{className:"flex w-full flex-col gap-2 md:max-w-xs",children:[T.jsx(Q.Input,{type:"password",label:"Neuer API-Key",value:Ie[he.id]??"",onChange:Ke=>go(he.id,Ke.target.value),disabled:_e||Ze===he.id}),T.jsxs("div",{className:"flex items-center gap-2 justify-end",children:[T.jsx(Q.Button,{variant:"text",color:"red",size:"sm",onClick:()=>Pa(he.id),disabled:_e||et===he.id||qt!==null||Ze===he.id,children:et===he.id?"Lösche…":"Löschen"}),T.jsx(Q.Button,{color:"blue",size:"sm",onClick:()=>Pt(he.id),disabled:_e||Ze===he.id,children:Ze===he.id?"Speichere…":"API-Key speichern"})]})]})]},he.id)})})]}),T.jsxs("div",{className:"flex flex-col gap-3" Line truncated
+2 -2
View File
@@ -34,8 +34,8 @@
<script defer data-site="YOUR_DOMAIN_HERE" src="https://api.nepcha.com/js/nepcha-analytics.js"></script>
<script type="module" crossorigin src="/assets/index-665b980d.js"></script>
<link rel="stylesheet" href="/assets/index-5f261614.css">
<script type="module" crossorigin src="/assets/index-8d2b4f50.js"></script>
<link rel="stylesheet" href="/assets/index-11780ccd.css">
</head>
<body>
<div id="root"></div>
+365 -5
View File
@@ -1,9 +1,11 @@
import { db } from '../db/index.js';
import { logEvent } from '../logging/eventLogs.js';
import {
hashPassword,
normalizeAvatarColor,
pickRandomAvatarColor,
DEFAULT_AVATAR_COLOR
DEFAULT_AVATAR_COLOR,
SUPERUSER_GROUP_NAME
} from '../auth/superuser.js';
const selectUsersWithGroups = db.prepare(`
@@ -42,6 +44,12 @@ const selectUserWithGroupsById = db.prepare(`
GROUP BY u.id
`);
const selectUserCredentialsById = db.prepare(`
SELECT id, username, email, password_hash, password_salt, avatar_color
FROM users
WHERE id = ?
`);
const selectUserByUsername = db.prepare('SELECT id FROM users WHERE username = ?');
const selectUserByEmail = db.prepare('SELECT id FROM users WHERE email = ?');
@@ -56,12 +64,39 @@ const insertMembershipForUser = db.prepare(`
`);
const selectGroupById = db.prepare('SELECT id, name FROM user_groups WHERE id = ?');
const selectGroupIdByName = db.prepare('SELECT id FROM user_groups WHERE lower(name) = lower(?) LIMIT 1');
const isUserInGroupStatement = db.prepare(`
SELECT 1 AS has_membership
FROM user_group_memberships m
INNER JOIN user_groups g ON g.id = m.group_id
WHERE m.user_id = ? AND lower(g.name) = lower(?)
LIMIT 1
`);
const insertUserStatement = db.prepare(`
INSERT INTO users (username, email, password_hash, password_salt, avatar_color, is_active, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
`);
const updateUserCoreStatement = db.prepare(`
UPDATE users
SET username = ?,
email = ?,
password_hash = ?,
password_salt = ?,
avatar_color = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`);
const updateUserActiveStatement = db.prepare(`
UPDATE users
SET is_active = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`);
const parseGroupPairs = (rawPairs) => {
if (!rawPairs) {
return [];
@@ -119,6 +154,18 @@ const insertUserWithGroups = db.transaction(({ username, email, passwordHash, pa
return userId;
});
const resolveActorFields = (actor) => {
if (!actor || actor.id === undefined || actor.id === null) {
return {};
}
const name = actor.username || actor.email || `User ${actor.id}`;
return {
actorType: 'user',
actorId: String(actor.id),
actorName: name
};
};
const normalizeEmail = (value) => {
if (!value) return null;
const trimmed = String(value).trim();
@@ -128,7 +175,8 @@ const normalizeEmail = (value) => {
return trimmed.toLowerCase();
};
export function createUser({ username, email, password, groupId, avatarColor }) {
export function createUser({ username, email, password, groupId, avatarColor }, options = {}) {
const actor = options.actor || null;
const normalizedUsername = typeof username === 'string' ? username.trim() : '';
if (!normalizedUsername) {
const error = new Error('USERNAME_REQUIRED');
@@ -150,6 +198,12 @@ export function createUser({ username, email, password, groupId, avatarColor })
throw error;
}
if ((groupRow.name || '').toLowerCase() === SUPERUSER_GROUP_NAME) {
const error = new Error('GROUP_SUPERUSER_PROTECTED');
error.code = 'GROUP_SUPERUSER_PROTECTED';
throw error;
}
const normalizedEmail = normalizeEmail(email);
if (normalizedEmail && !normalizedEmail.includes('@')) {
const error = new Error('INVALID_EMAIL');
@@ -200,10 +254,30 @@ export function createUser({ username, email, password, groupId, avatarColor })
groupId: numericGroupId
});
return getUserById(userId);
const userRecord = getUserById(userId);
logEvent({
category: 'benutzer',
eventType: 'benutzer-angelegt',
action: 'anlegen',
status: 'erfolgreich',
entityType: 'benutzer',
entityId: String(userRecord?.id ?? userId),
entityName: userRecord?.username ?? normalizedUsername,
message: `Benutzer "${normalizedUsername}" angelegt`,
metadata: {
email: userRecord?.email ?? normalizedEmail ?? null,
primaryGroupId: numericGroupId,
primaryGroupName: groupRow.name
},
...resolveActorFields(actor)
});
return userRecord;
}
export function updateUserGroups(userId, groupIds) {
export function updateUserGroups(userId, groupIds, options = {}) {
const actor = options.actor || null;
const numericUserId = Number(userId);
if (!Number.isFinite(numericUserId) || numericUserId <= 0) {
const error = new Error('INVALID_USER_ID');
@@ -229,10 +303,16 @@ export function updateUserGroups(userId, groupIds) {
: [];
const missingGroupIds = [];
const groupDetails = [];
normalizedGroupIds.forEach((groupId) => {
const groupRow = selectGroupById.get(groupId);
if (!groupRow) {
missingGroupIds.push(groupId);
} else {
groupDetails.push({
id: groupId,
name: groupRow.name
});
}
});
@@ -244,5 +324,285 @@ export function updateUserGroups(userId, groupIds) {
}
applyUserGroupAssignments(numericUserId, normalizedGroupIds);
return getUserById(numericUserId);
const updated = getUserById(numericUserId);
logEvent({
category: 'benutzer',
eventType: 'benutzer-gruppen-aktualisiert',
action: 'gruppe-aktualisieren',
status: 'erfolgreich',
entityType: 'benutzer',
entityId: String(numericUserId),
entityName: updated?.username ?? existingUser.username ?? `ID ${numericUserId}`,
message: `Gruppenzuordnung für Benutzer "${updated?.username ?? existingUser.username ?? numericUserId}" aktualisiert`,
metadata: {
previousGroups: (existingUser.groups || []).map((group) => ({ id: group.id, name: group.name })),
groups: updated?.groups?.map((group) => ({ id: group.id, name: group.name })) ?? groupDetails
},
...resolveActorFields(actor)
});
return updated;
}
export function updateUserDetails(userId, { username, email, password, avatarColor, groupId, groupIds } = {}, options = {}) {
const actor = options.actor || null;
const numericUserId = Number(userId);
if (!Number.isFinite(numericUserId) || numericUserId <= 0) {
const error = new Error('INVALID_USER_ID');
error.code = 'INVALID_USER_ID';
throw error;
}
const existingUser = selectUserCredentialsById.get(numericUserId);
if (!existingUser) {
const error = new Error('USER_NOT_FOUND');
error.code = 'USER_NOT_FOUND';
throw error;
}
const previousUserRecord = getUserById(numericUserId);
const normalizedUsername = typeof username === 'string' ? username.trim() : existingUser.username;
if (!normalizedUsername) {
const error = new Error('USERNAME_REQUIRED');
error.code = 'USERNAME_REQUIRED';
throw error;
}
const usernameRow = selectUserByUsername.get(normalizedUsername);
if (usernameRow && Number(usernameRow.id) !== numericUserId) {
const error = new Error('USERNAME_TAKEN');
error.code = 'USERNAME_TAKEN';
throw error;
}
const normalizedEmail = email === undefined ? existingUser.email : normalizeEmail(email);
if (normalizedEmail && !normalizedEmail.includes('@')) {
const error = new Error('INVALID_EMAIL');
error.code = 'INVALID_EMAIL';
throw error;
}
if (normalizedEmail) {
const emailRow = selectUserByEmail.get(normalizedEmail);
if (emailRow && Number(emailRow.id) !== numericUserId) {
const error = new Error('EMAIL_TAKEN');
error.code = 'EMAIL_TAKEN';
throw error;
}
}
let passwordHash = existingUser.password_hash;
let passwordSalt = existingUser.password_salt;
if (typeof password === 'string') {
const trimmedPassword = password.trim();
if (trimmedPassword.length > 0) {
const hashed = hashPassword(trimmedPassword);
passwordHash = hashed.hash;
passwordSalt = hashed.salt;
}
} else if (password !== undefined && password !== null) {
const error = new Error('INVALID_PASSWORD');
error.code = 'INVALID_PASSWORD';
throw error;
}
let colorToPersist = existingUser.avatar_color || DEFAULT_AVATAR_COLOR;
if (avatarColor !== undefined) {
const candidate = String(avatarColor || '').trim();
if (!candidate) {
colorToPersist = DEFAULT_AVATAR_COLOR;
} else {
const normalizedColor = normalizeAvatarColor(candidate);
if (!normalizedColor) {
const error = new Error('INVALID_AVATAR_COLOR');
error.code = 'INVALID_AVATAR_COLOR';
throw error;
}
colorToPersist = normalizedColor;
}
}
const shouldUpdateGroups = groupId !== undefined || groupIds !== undefined;
let normalizedGroupIds = null;
let nextGroups = null;
if (shouldUpdateGroups) {
const incomingGroupIds = Array.isArray(groupIds)
? groupIds
: groupId !== undefined && groupId !== null
? [groupId]
: [];
normalizedGroupIds = Array.from(
new Set(
incomingGroupIds
.map((value) => Number(value))
.filter((value) => Number.isFinite(value) && value > 0)
)
);
const missingGroupIds = [];
nextGroups = [];
normalizedGroupIds.forEach((value) => {
const groupRow = selectGroupById.get(value);
if (!groupRow) {
missingGroupIds.push(value);
} else {
nextGroups.push({ id: value, name: groupRow.name });
}
});
if (missingGroupIds.length > 0) {
const error = new Error('GROUP_NOT_FOUND');
error.code = 'GROUP_NOT_FOUND';
error.missingGroupIds = missingGroupIds;
throw error;
}
}
const performUpdate = db.transaction(() => {
updateUserCoreStatement.run(
normalizedUsername,
normalizedEmail,
passwordHash,
passwordSalt,
colorToPersist,
numericUserId
);
if (normalizedGroupIds !== null) {
applyUserGroupAssignments(numericUserId, normalizedGroupIds);
}
});
performUpdate();
const updatedUser = getUserById(numericUserId);
logEvent({
category: 'benutzer',
eventType: 'benutzer-aktualisiert',
action: 'aktualisieren',
status: 'erfolgreich',
entityType: 'benutzer',
entityId: String(numericUserId),
entityName: updatedUser?.username ?? normalizedUsername,
message: `Benutzer "${updatedUser?.username ?? normalizedUsername}" aktualisiert`,
metadata: {
email: updatedUser?.email ?? normalizedEmail ?? null,
groupsUpdated: normalizedGroupIds !== null
? (updatedUser?.groups?.map((group) => ({ id: group.id, name: group.name })) ?? nextGroups)
: undefined,
previousGroups: normalizedGroupIds !== null
? (previousUserRecord?.groups || []).map((group) => ({ id: group.id, name: group.name }))
: undefined,
avatarColor: updatedUser?.avatarColor ?? colorToPersist
},
...resolveActorFields(actor)
});
return updatedUser;
}
const deleteMembershipsStatement = db.prepare(`
DELETE FROM user_group_memberships
WHERE user_id = ?
`);
const deleteUserStatement = db.prepare(`
DELETE FROM users
WHERE id = ?
`);
export function deleteUser(userId, options = {}) {
const actor = options.actor || null;
const numericUserId = Number(userId);
if (!Number.isFinite(numericUserId) || numericUserId <= 0) {
const error = new Error('INVALID_USER_ID');
error.code = 'INVALID_USER_ID';
throw error;
}
const existingUser = selectUserCredentialsById.get(numericUserId);
if (!existingUser) {
const error = new Error('USER_NOT_FOUND');
error.code = 'USER_NOT_FOUND';
throw error;
}
const superuserMembership = isUserInGroupStatement.get(numericUserId, SUPERUSER_GROUP_NAME);
if (superuserMembership && superuserMembership.has_membership) {
const error = new Error('USER_SUPERUSER_PROTECTED');
error.code = 'USER_SUPERUSER_PROTECTED';
throw error;
}
const performDelete = db.transaction(() => {
deleteMembershipsStatement.run(numericUserId);
deleteUserStatement.run(numericUserId);
});
performDelete();
logEvent({
category: 'benutzer',
eventType: 'benutzer-gelöscht',
action: 'gelöscht',
status: 'erfolgreich',
entityType: 'benutzer',
entityId: String(numericUserId),
entityName: existingUser.username ?? `ID ${numericUserId}`,
message: `Benutzer "${existingUser.username ?? numericUserId}" gelöscht`,
metadata: {
email: existingUser.email ?? null
},
...resolveActorFields(actor)
});
}
export function updateUserActiveStatus(userId, isActive, options = {}) {
const actor = options.actor || null;
const numericUserId = Number(userId);
if (!Number.isFinite(numericUserId) || numericUserId <= 0) {
const error = new Error('INVALID_USER_ID');
error.code = 'INVALID_USER_ID';
throw error;
}
const existingUser = selectUserCredentialsById.get(numericUserId);
if (!existingUser) {
const error = new Error('USER_NOT_FOUND');
error.code = 'USER_NOT_FOUND';
throw error;
}
const superuserMembership = isUserInGroupStatement.get(numericUserId, SUPERUSER_GROUP_NAME);
if (superuserMembership && superuserMembership.has_membership && !isActive) {
const error = new Error('USER_SUPERUSER_PROTECTED');
error.code = 'USER_SUPERUSER_PROTECTED';
throw error;
}
const normalizedIsActive = isActive ? 1 : 0;
updateUserActiveStatement.run(normalizedIsActive, numericUserId);
const updated = getUserById(numericUserId);
logEvent({
category: 'benutzer',
eventType: normalizedIsActive ? 'benutzer-aktiviert' : 'benutzer-deaktiviert',
action: 'status-aktualisieren',
status: 'erfolgreich',
entityType: 'benutzer',
entityId: String(numericUserId),
entityName: updated?.username ?? existingUser.username ?? `ID ${numericUserId}`,
message: normalizedIsActive
? `Benutzer "${updated?.username ?? existingUser.username ?? numericUserId}" aktiviert`
: `Benutzer "${updated?.username ?? existingUser.username ?? numericUserId}" deaktiviert`,
metadata: {
isActive: Boolean(normalizedIsActive)
},
...resolveActorFields(actor)
});
return updated;
}
+2
View File
@@ -10,6 +10,7 @@ import {
} from "@/widgets/layout";
import routes from "@/routes";
import { UserDetails } from "@/pages/dashboard/userDetails.jsx";
import { UserGroupDetail } from "@/pages/dashboard/userGroupDetail.jsx";
import { useMaterialTailwindController, setOpenConfigurator } from "@/components";
export function Dashboard() {
@@ -156,6 +157,7 @@ export function Dashboard() {
))
)}
<Route path="users/:userId" element={<UserDetails />} />
<Route path="usergroups/:groupId" element={<UserGroupDetail />} />
</Routes>
<div className="text-blue-gray-600">
<Footer />
+4 -7
View File
@@ -90,27 +90,24 @@ export function SignIn() {
body: JSON.stringify({ identifier: trimmedIdentifier, password: trimmedPassword }),
});
if (response.status === 403) {
navigate("/setup", { replace: true });
return;
}
const payload = await response.json().catch(() => ({}));
if (!response.ok) {
const payload = await response.json().catch(() => ({}));
if (response.status === 401 || payload.error === "INVALID_CREDENTIALS") {
setError("Ungültige Zugangsdaten.");
} else if (payload.error === "MISSING_CREDENTIALS") {
setError("Bitte fülle beide Felder aus.");
} else if (payload.error === "SETUP_REQUIRED") {
} else if (payload.error === "SETUP_REQUIRED" || payload.error === "SUPERUSER_REQUIRED") {
navigate("/setup", { replace: true });
return;
} else if (payload.error === "USER_INACTIVE") {
setError("Dieses Konto ist deaktiviert. Bitte wende dich an einen Administrator.");
} else {
setError("Anmeldung fehlgeschlagen. Bitte versuche es erneut.");
}
return;
}
await response.json().catch(() => ({}));
navigate("/dashboard/stacks", { replace: true });
} catch (err) {
console.error("⚠️ [Auth] Anmeldung fehlgeschlagen:", err);
+1
View File
@@ -4,3 +4,4 @@ export * from "@/pages/dashboard/logs";
export * from "@/pages/dashboard/users";
export * from "@/pages/dashboard/usergroups";
export * from "@/pages/dashboard/userDetails";
export * from "@/pages/dashboard/userGroupDetail";
File diff suppressed because it is too large. Load diff
@@ -1,88 +0,0 @@
import React from "react";
import {
Typography,
Alert,
Card,
CardHeader,
CardBody,
} from "@material-tailwind/react";
import { InformationCircleIcon } from "@heroicons/react/24/outline";
export function Notifications() {
const [showAlerts, setShowAlerts] = React.useState({
blue: true,
green: true,
orange: true,
red: true,
});
const [showAlertsWithIcon, setShowAlertsWithIcon] = React.useState({
blue: true,
green: true,
orange: true,
red: true,
});
const alerts = ["gray", "green", "orange", "red"];
return (
<div className="mx-auto my-20 flex max-w-screen-lg flex-col gap-8">
<Card>
<CardHeader
color="transparent"
floated={false}
shadow={false}
className="m-0 p-4"
>
<Typography variant="h5" color="blue-gray">
Alerts
</Typography>
</CardHeader>
<CardBody className="flex flex-col gap-4 p-4">
{alerts.map((color) => (
<Alert
key={color}
open={showAlerts[color]}
color={color}
onClose={() => setShowAlerts((current) => ({ ...current, [color]: false }))}
>
A simple {color} alert with an <a href="#">example link</a>. Give
it a click if you like.
</Alert>
))}
</CardBody>
</Card>
<Card>
<CardHeader
color="transparent"
floated={false}
shadow={false}
className="m-0 p-4"
>
<Typography variant="h5" color="blue-gray">
Alerts with Icon
</Typography>
</CardHeader>
<CardBody className="flex flex-col gap-4 p-4">
{alerts.map((color) => (
<Alert
key={color}
open={showAlertsWithIcon[color]}
color={color}
icon={
<InformationCircleIcon strokeWidth={2} className="h-6 w-6" />
}
onClose={() => setShowAlertsWithIcon((current) => ({
...current,
[color]: false,
}))}
>
A simple {color} alert with an <a href="#">example link</a>. Give
it a click if you like.
</Alert>
))}
</CardBody>
</Card>
</div>
);
}
export default Notifications;
-221
View File
@@ -1,221 +0,0 @@
import {
Card,
CardBody,
CardHeader,
CardFooter,
Avatar,
Typography,
Tabs,
TabsHeader,
Tab,
Switch,
Tooltip,
Button,
} from "@material-tailwind/react";
import {
HomeIcon,
ChatBubbleLeftEllipsisIcon,
Cog6ToothIcon,
PencilIcon,
} from "@heroicons/react/24/solid";
import { Link } from "react-router-dom";
import { ProfileInfoCard, MessageCard } from "@/widgets/cards";
import { platformSettingsData, conversationsData, projectsData } from "@/data";
export function Profile() {
return (
<>
<div className="relative mt-8 h-72 w-full overflow-hidden rounded-xl bg-[url('/img/background-image.png')] bg-cover bg-center">
<div className="absolute inset-0 h-full w-full bg-gray-900/75" />
</div>
<Card className="mx-3 -mt-16 mb-6 lg:mx-4 border border-blue-gray-100">
<CardBody className="p-4">
<div className="mb-10 flex items-center justify-between flex-wrap gap-6">
<div className="flex items-center gap-6">
<Avatar
src="/img/bruce-mars.jpeg"
alt="bruce-mars"
size="xl"
variant="rounded"
className="rounded-lg shadow-lg shadow-blue-gray-500/40"
/>
<div>
<Typography variant="h5" color="blue-gray" className="mb-1">
Richard Davis
</Typography>
<Typography
variant="small"
className="font-normal text-blue-gray-600"
>
CEO / Co-Founder
</Typography>
</div>
</div>
<div className="w-96">
<Tabs value="app">
<TabsHeader>
<Tab value="app">
<HomeIcon className="-mt-1 mr-2 inline-block h-5 w-5" />
App
</Tab>
<Tab value="message">
<ChatBubbleLeftEllipsisIcon className="-mt-0.5 mr-2 inline-block h-5 w-5" />
Message
</Tab>
<Tab value="settings">
<Cog6ToothIcon className="-mt-1 mr-2 inline-block h-5 w-5" />
Settings
</Tab>
</TabsHeader>
</Tabs>
</div>
</div>
<div className="gird-cols-1 mb-12 grid gap-12 px-4 lg:grid-cols-2 xl:grid-cols-3">
<div>
<Typography variant="h6" color="blue-gray" className="mb-3">
Platform Settings
</Typography>
<div className="flex flex-col gap-12">
{platformSettingsData.map(({ title, options }) => (
<div key={title}>
<Typography className="mb-4 block text-xs font-semibold uppercase text-blue-gray-500">
{title}
</Typography>
<div className="flex flex-col gap-6">
{options.map(({ checked, label }) => (
<Switch
key={label}
id={label}
label={label}
defaultChecked={checked}
labelProps={{
className: "text-sm font-normal text-blue-gray-500",
}}
/>
))}
</div>
</div>
))}
</div>
</div>
<ProfileInfoCard
title="Profile Information"
description="Hi, I'm Alec Thompson, Decisions: If you can't decide, the answer is no. If two equally difficult paths, choose the one more painful in the short term (pain avoidance is creating an illusion of equality)."
details={{
"first name": "Alec M. Thompson",
mobile: "(44) 123 1234 123",
email: "alecthompson@mail.com",
location: "USA",
social: (
<div className="flex items-center gap-4">
<i className="fa-brands fa-facebook text-blue-700" />
<i className="fa-brands fa-twitter text-blue-400" />
<i className="fa-brands fa-instagram text-purple-500" />
</div>
),
}}
action={
<Tooltip content="Edit Profile">
<PencilIcon className="h-4 w-4 cursor-pointer text-blue-gray-500" />
</Tooltip>
}
/>
<div>
<Typography variant="h6" color="blue-gray" className="mb-3">
Platform Settings
</Typography>
<ul className="flex flex-col gap-6">
{conversationsData.map((props) => (
<MessageCard
key={props.name}
{...props}
action={
<Button variant="text" size="sm">
reply
</Button>
}
/>
))}
</ul>
</div>
</div>
<div className="px-4 pb-4">
<Typography variant="h6" color="blue-gray" className="mb-2">
Projects
</Typography>
<Typography
variant="small"
className="font-normal text-blue-gray-500"
>
Architects design houses
</Typography>
<div className="mt-6 grid grid-cols-1 gap-12 md:grid-cols-2 xl:grid-cols-4">
{projectsData.map(
({ img, title, description, tag, route, members }) => (
<Card key={title} color="transparent" shadow={false}>
<CardHeader
floated={false}
color="gray"
className="mx-0 mt-0 mb-4 h-64 xl:h-40"
>
<img
src={img}
alt={title}
className="h-full w-full object-cover"
/>
</CardHeader>
<CardBody className="py-0 px-1">
<Typography
variant="small"
className="font-normal text-blue-gray-500"
>
{tag}
</Typography>
<Typography
variant="h5"
color="blue-gray"
className="mt-1 mb-2"
>
{title}
</Typography>
<Typography
variant="small"
className="font-normal text-blue-gray-500"
>
{description}
</Typography>
</CardBody>
<CardFooter className="mt-6 flex items-center justify-between py-0 px-1">
<Link to={route}>
<Button variant="outlined" size="sm">
view project
</Button>
</Link>
<div>
{members.map(({ img, name }, key) => (
<Tooltip key={name} content={name}>
<Avatar
src={img}
alt={name}
size="xs"
variant="circular"
className={`cursor-pointer border-2 border-white ${
key === 0 ? "" : "-ml-2.5"
}`}
/>
</Tooltip>
))}
</div>
</CardFooter>
</Card>
)
)}
</div>
</div>
</CardBody>
</Card>
</>
);
}
export default Profile;
+368 -270
View File
@@ -1,34 +1,25 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import axios from "axios";
import { useParams } from "react-router-dom";
import {
Card,
CardHeader,
CardBody,
Typography,
Button,
Spinner,
CardFooter,
Switch,
Tooltip,
Avatar,
Select,
Option
Option,
Input,
Switch
} from "@material-tailwind/react";
import {
PencilIcon,
} from "@heroicons/react/24/outline";
import { Link } from "react-router-dom";
import { useToast } from "@/components/ToastProvider.jsx";
import { ProfileInfoCard } from "@/widgets/cards";
import { platformSettingsData, projectsData } from "@/data";
import { platformSettingsData } from "@/data";
import { AVATAR_COLORS } from "@/data/avatarColors.js";
import { useMaintenance } from "@/components/MaintenanceProvider.jsx";
const _ = AVATAR_COLORS.join(" ");
const normalizeUserGroups = (rawGroups) => {
if (!Array.isArray(rawGroups)) {
return [];
@@ -67,47 +58,74 @@ const mapUser = (item) => ({
groups: normalizeUserGroups(item?.groups)
});
const extractPrimaryGroupId = (user) => {
if (!user || !Array.isArray(user.groups) || user.groups.length === 0) {
return null;
}
const firstValid = user.groups
.map((group) => Number(group.id))
.find((id) => Number.isFinite(id) && id > 0);
return Number.isFinite(firstValid) ? firstValid : null;
};
const buildInitialFormValues = (user) => {
if (!user) {
return {
username: "",
email: "",
password: "",
groupId: null,
avatarColor: ""
};
}
const primaryGroupId = extractPrimaryGroupId(user);
return {
username: user.username || "",
email: user.email || "",
password: "",
groupId: Number.isFinite(primaryGroupId) ? primaryGroupId : null,
avatarColor: user.avatarColor || ""
};
};
export function UserDetails() {
const { userId } = useParams();
const { showToast } = useToast();
const { maintenance } = useMaintenance();
const [user, setUser] = useState(null);
const [formValues, setFormValues] = useState(buildInitialFormValues(null));
const initialFormValuesRef = useRef(buildInitialFormValues(null));
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const [hasLoaded, setHasLoaded] = useState(false);
const [availableGroups, setAvailableGroups] = useState([]);
const [groupsLoading, setGroupsLoading] = useState(false);
const [groupsError, setGroupsError] = useState("");
const [selectedGroupId, setSelectedGroupId] = useState(null);
const [savingGroup, setSavingGroup] = useState(false);
const [savingUser, setSavingUser] = useState(false);
const [saveError, setSaveError] = useState("");
const maintenanceActive = Boolean(maintenance?.active);
const isSuperuserUser = useMemo(() => {
if (!Array.isArray(user?.groups)) {
return false;
}
return user.groups.some((group) => (group?.name || "").toLowerCase() === "superuser");
}, [user]);
const numericUserId = useMemo(() => {
const asNumber = Number(userId);
return Number.isFinite(asNumber) ? asNumber : null;
}, [userId]);
const formatTimestamp = useCallback((value) => {
if (!value) {
return "–";
}
const normalized = typeof value === "string" ? value.replace(" ", "T") : value;
const parsed = new Date(normalized);
if (Number.isNaN(parsed.getTime())) {
return value;
}
return new Intl.DateTimeFormat("de-DE", {
dateStyle: "short",
timeStyle: "short"
}).format(parsed);
}, []);
const fetchUserDetails = useCallback(async () => {
if (!numericUserId) {
setError("Ungültige Benutzer-ID.");
setUser(null);
setFormValues(buildInitialFormValues(null));
initialFormValuesRef.current = buildInitialFormValues(null);
setHasLoaded(true);
return;
}
@@ -122,7 +140,10 @@ export function UserDetails() {
throw new Error("USER_NOT_FOUND");
}
setUser(item);
setError("");
const initialValues = buildInitialFormValues(item);
initialFormValuesRef.current = { ...initialValues };
setFormValues(initialValues);
setSaveError("");
} catch (err) {
const serverError = err.response?.data?.error;
let message = "Benutzerdetails konnten nicht geladen werden.";
@@ -136,6 +157,8 @@ export function UserDetails() {
}
setUser(null);
initialFormValuesRef.current = buildInitialFormValues(null);
setFormValues(buildInitialFormValues(null));
setError(message);
showToast({
variant: "error",
@@ -185,130 +208,245 @@ export function UserDetails() {
fetchAvailableGroups();
}, [fetchAvailableGroups]);
const originalGroupId = useMemo(() => {
if (!user || !Array.isArray(user.groups) || user.groups.length === 0) {
return null;
}
const firstValid = user.groups
.map((group) => Number(group.id))
.find((id) => Number.isFinite(id) && id > 0);
return Number.isFinite(firstValid) ? firstValid : null;
}, [user]);
useEffect(() => {
setSelectedGroupId(originalGroupId);
}, [originalGroupId]);
const hasGroupChanges = useMemo(() => {
if (!hasLoaded) {
const hasChanges = useMemo(() => {
if (!hasLoaded || !user) {
return false;
}
return originalGroupId !== selectedGroupId;
}, [originalGroupId, selectedGroupId, hasLoaded]);
const initial = initialFormValuesRef.current;
if (!initial) {
return false;
}
const initialUsername = initial.username || "";
const currentUsername = formValues.username || "";
const initialEmail = initial.email || "";
const currentEmail = formValues.email || "";
const initialGroupId = Number.isFinite(initial.groupId) ? Number(initial.groupId) : null;
const currentGroupId = Number.isFinite(formValues.groupId) ? Number(formValues.groupId) : null;
const initialAvatar = initial.avatarColor || "";
const currentAvatar = formValues.avatarColor || "";
const passwordChanged = Boolean(formValues.password && formValues.password.trim().length > 0);
return (
initialUsername !== currentUsername ||
initialEmail !== currentEmail ||
(!isSuperuserUser && initialGroupId !== currentGroupId) ||
initialAvatar !== currentAvatar ||
passwordChanged
);
}, [formValues, hasLoaded, user, isSuperuserUser]);
const renderSelectedAvatarLabel = useCallback(
(element) => {
if (element?.props?.children) {
return element.props.children;
}
if (!formValues.avatarColor) {
return "Standardfarbe";
}
return formValues.avatarColor;
},
[formValues.avatarColor]
);
const handleUsernameChange = useCallback((event) => {
const { value } = event.target;
setFormValues((prev) => ({
...prev,
username: value
}));
}, []);
const handleEmailChange = useCallback((event) => {
const { value } = event.target;
setFormValues((prev) => ({
...prev,
email: value
}));
}, []);
const handlePasswordChange = useCallback((event) => {
const { value } = event.target;
setFormValues((prev) => ({
...prev,
password: value
}));
}, []);
const handleGroupChange = useCallback((value) => {
if (!value) {
setSelectedGroupId(null);
setFormValues((prev) => ({
...prev,
groupId: null
}));
return;
}
const numeric = Number(value);
setSelectedGroupId(Number.isFinite(numeric) && numeric > 0 ? numeric : null);
setFormValues((prev) => ({
...prev,
groupId: Number.isFinite(numeric) && numeric > 0 ? numeric : null
}));
}, []);
const handleResetGroup = useCallback(() => {
setSelectedGroupId(originalGroupId);
}, [originalGroupId]);
const handleAvatarColorChange = useCallback((value) => {
setFormValues((prev) => ({
...prev,
avatarColor: value || ""
}));
}, []);
const handleSaveGroup = useCallback(async () => {
if (!user || !hasGroupChanges) {
const handleSaveUser = useCallback(async () => {
if (!user || !hasChanges) {
return;
}
const payloadGroups = selectedGroupId ? [selectedGroupId] : [];
setSavingUser(true);
setSaveError("");
setSavingGroup(true);
setGroupsError("");
try {
const response = await axios.put(`/api/users/${user.id}/groups`, { groupIds: payloadGroups });
const payload = {
username: formValues.username,
email: formValues.email,
password: formValues.password,
avatarColor: formValues.avatarColor
};
if (!isSuperuserUser) {
payload.groupId = formValues.groupId;
}
const response = await axios.put(`/api/users/${user.id}`, payload);
const updatedUser = mapUser(response.data?.item || response.data?.user);
setUser(updatedUser);
const nextGroupId = Array.isArray(updatedUser.groups) && updatedUser.groups.length > 0
? Number(updatedUser.groups[0].id)
: null;
setSelectedGroupId(Number.isFinite(nextGroupId) && nextGroupId > 0 ? nextGroupId : null);
const nextInitial = buildInitialFormValues(updatedUser);
initialFormValuesRef.current = { ...nextInitial };
setFormValues(nextInitial);
setGroupsError("");
showToast({
variant: "success",
title: "Globale Rolle aktualisiert",
description: "Die Gruppenzuordnung wurde gespeichert."
title: "Benutzer gespeichert",
description: "Die Änderungen wurden erfolgreich gespeichert."
});
} catch (err) {
const serverError = err.response?.data?.error;
let message = err.message || "Die globale Rolle konnte nicht aktualisiert werden.";
if (serverError === "INVALID_USER_ID") {
message = "Die Benutzer-ID ist ungültig.";
} else if (serverError === "USER_NOT_FOUND") {
message = "Der Benutzer konnte nicht gefunden werden.";
let message = "Die Benutzerdaten konnten nicht gespeichert werden.";
if (serverError === "USERNAME_REQUIRED") {
message = "Bitte einen Benutzernamen angeben.";
} else if (serverError === "USERNAME_TAKEN") {
message = "Der Benutzername wird bereits verwendet.";
} else if (serverError === "INVALID_EMAIL") {
message = "Bitte eine gültige E-Mail-Adresse eingeben.";
} else if (serverError === "EMAIL_TAKEN") {
message = "Die E-Mail-Adresse wird bereits verwendet.";
} else if (serverError === "INVALID_PASSWORD") {
message = "Das Passwort ist ungültig.";
} else if (serverError === "PASSWORD_TOO_SHORT") {
message = "Das Passwort muss mindestens 8 Zeichen enthalten.";
} else if (serverError === "INVALID_AVATAR_COLOR") {
message = "Bitte eine gültige Avatar-Farbe auswählen.";
} else if (serverError === "GROUP_NOT_FOUND") {
message = "Die ausgewählte Gruppe existiert nicht mehr.";
} else if (typeof serverError === "string" && serverError.length > 0) {
message = serverError;
}
message = "Die ausgewählte Benutzergruppe existiert nicht mehr.";
setGroupsError(message);
}
setSaveError(message);
showToast({
variant: "error",
title: "Globale Rolle",
title: "Speichern fehlgeschlagen",
description: message
});
} finally {
setSavingGroup(false);
setSavingUser(false);
}
}, [user, selectedGroupId, hasGroupChanges, showToast]);
const selectedGroupLabel = useMemo(() => {
if (!selectedGroupId) {
return null;
}
const match = availableGroups.find((group) => group.id === selectedGroupId);
return match ? match.name : null;
}, [availableGroups, selectedGroupId]);
const selectValue = selectedGroupId ? String(selectedGroupId) : "";
const selectDisabled = maintenanceActive || savingGroup || !user || groupsLoading;
}, [user, hasChanges, formValues, showToast, isSuperuserUser]);
const avatarLabel = useMemo(() => {
const source = (user?.username || user?.email || "").trim();
const source = (formValues.username || formValues.email || "").trim();
if (!source) {
return "?";
}
return source.charAt(0).toUpperCase();
}, [user]);
}, [formValues.username, formValues.email]);
const avatarColorClass = useMemo(() => {
if (formValues.avatarColor) {
return formValues.avatarColor;
}
return user?.avatarColor || "";
}, [formValues.avatarColor, user]);
const filteredGroups = useMemo(() => {
if (!Array.isArray(availableGroups)) {
return [];
}
return availableGroups.filter((group) => (group?.name || "").toLowerCase() !== "superuser");
}, [availableGroups]);
useEffect(() => {
if (isSuperuserUser) {
return;
}
const currentGroupId = formValues.groupId ? Number(formValues.groupId) : null;
const stillExists = filteredGroups.some((group) => Number(group.id) === currentGroupId);
if (!stillExists) {
setFormValues((prev) => ({
...prev,
groupId: null
}));
}
}, [filteredGroups, formValues.groupId, isSuperuserUser]);
const selectDisabled = maintenanceActive || savingUser || !user || groupsLoading;
const avatarSelectDisabled = maintenanceActive || savingUser || !user;
const inputDisabled = maintenanceActive || savingUser || !user;
const groupSelectValue = formValues.groupId ? String(formValues.groupId) : "";
return (
<>
<div className="relative mt-8 h-72 w-full overflow-hidden rounded-xl bg-[url('/img/background-image.png')] bg-cover bg-center">
<div className="mt-12 flex flex-col gap-12">
{maintenanceActive && (
<div className="mb-3 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
Wartungsmodus aktiv – Änderungen sind deaktiviert.
</div>
)}
<div className="relative h-72 w-full overflow-hidden rounded-xl bg-[url('/img/background-image.png')] bg-cover\tbg-center">
<div className="absolute inset-0 h-full w-full bg-gray-900/75" />
</div>
<Card className="mx-3 -mt-16 mb-6 lg:mx-4 border border-blue-gray-100">
<CardBody className="p-4">
<div className="mb-10 flex items-center justify-between flex-wrap gap-6">
<div className="mb-10 flex flex-wrap items-center justify-between gap-6">
<div className="flex items-center gap-6">
<div
className={`text-black flex h-[74px] w-[74px] items-center justify-center rounded-xl text-3xl font-semibold uppercase shadow-lg shadow-blue-gray-500/40 ${user?.avatarColor}`}
aria-label={user?.username || "Benutzeravatar"}
className={`text-black flex h-[74px] w-[74px] items-center justify-center rounded-xl text-3xl font-semibold uppercase shadow-lg shadow-blue-gray-500/40 ${avatarColorClass}`}
aria-label={formValues.username || "Benutzeravatar"}
>
{avatarLabel}
</div>
<div>
<Typography variant="h5" color="blue-gray" className="mb-1">
{user?.username}
<Typography variant="h5" color="blue-gray">
{formValues.username || "–"}
</Typography>
<Typography className="text-xs font-semibold uppercase tracking-wide text-stormGrey-400">
User-ID: {user?.id || "–"}
<Typography className="text-xs font-semibold tracking-wide text-stormGrey-400">
{formValues.email || "–"}
</Typography>
</div>
</div>
{hasChanges && (
<Button
color="green"
className="normal-case"
onClick={handleSaveUser}
disabled={maintenanceActive || savingUser}
>
{savingUser ? "Speichert ..." : "Änderungen speichern"}
</Button>
)}
</div>
{loading && !user && (
<div className="mb-6 flex items-center gap-3 rounded-lg border border-blue-gray-50 bg-blue-gray-50/50 px-4 py-3 text-sm text-blue-gray-500">
@@ -321,7 +459,133 @@ export function UserDetails() {
{error}
</div>
)}
<div className="gird-cols-1 mb-12 grid gap-12 px-4 lg:grid-cols-2 xl:grid-cols-3">
{saveError && !loading && (
<div className="mb-6 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800">
{saveError}
</div>
)}
<div className="grid-cols-1 mb-12 grid gap-12 px-4 lg:grid-cols-2 xl:grid-cols-3">
<div>
<Typography variant="h6" color="blue-gray" className="mb-4">
Allgemeine Einstellungen
</Typography>
<div className="mb-6">
<Typography className="mb-2 block text-xs font-semibold uppercase text-blue-gray-500">
Benutzername
</Typography>
<Input
value={formValues.username}
onChange={handleUsernameChange}
placeholder="Benutzername"
disabled={inputDisabled}
className=" !border-t-blue-gray-200 focus:!border-t-gray-900"
labelProps={{
className: "before:content-none after:content-none"
}}
/>
</div>
<div className="mb-6">
<Typography className="mb-2 block text-xs font-semibold uppercase text-blue-gray-500">
E-Mail-Adresse
</Typography>
<Input
type="email"
value={formValues.email}
onChange={handleEmailChange}
placeholder="benutzer@example.com"
disabled={inputDisabled}
className=" !border-t-blue-gray-200 focus:!border-t-gray-900"
labelProps={{
className: "before:content-none after:content-none"
}}
/>
</div>
<div className="mb-6">
<Typography className="mb-2 block text-xs font-semibold uppercase text-blue-gray-500">
Neues Passwort
</Typography>
<Input
type="password"
value={formValues.password}
onChange={handlePasswordChange}
placeholder="Passwort setzen"
disabled={inputDisabled}
className=" !border-t-blue-gray-200 focus:!border-t-gray-900"
labelProps={{
className: "before:content-none after:content-none"
}}
/>
<Typography className="mt-1 text-xs text-blue-gray-400">
Das Passwort wird nur geändert, wenn ein neuer Wert eingetragen wird.
</Typography>
</div>
<div className="mb-6">
<Typography className="mb-2 block text-xs font-semibold uppercase text-blue-gray-500">
Globale Rolle
</Typography>
{groupsError && !isSuperuserUser && (
<div className="mb-3 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800">
{groupsError}
</div>
)}
{isSuperuserUser ? (
<Typography className="block text-xs font-semibold uppercase text-blue-gray-500">
Die Rolle für den Superuser kann nicht geändert werden.
</Typography>
) : groupsLoading && availableGroups.length === 0 ? (
<div className="flex items-center gap-3 rounded-lg border border-blue-gray-50 bg-blue-gray-50/50 px-4 py-3 text-sm text-blue-gray-500">
<Spinner className="h-4 w-4" />
<span>Benutzergruppen werden geladen ...</span>
</div>
) : filteredGroups.length === 0 ? (
<Typography className="block text-xs font-semibold uppercase text-blue-gray-500">
Es sind keine weiteren Benutzergruppen vorhanden. Bitte lege zunächst eine neue Gruppe an.
</Typography>
) : (
<Select
label="Benutzergruppe wählen"
value={groupSelectValue}
onChange={handleGroupChange}
disabled={selectDisabled}
variant="outlined"
>
{filteredGroups.map((group) => (
<Option key={group.id} value={String(group.id)}>
{group.name}
</Option>
))}
</Select>
)}
</div>
<div className="mb-6">
<Typography className="mb-2 block text-xs font-semibold uppercase text-blue-gray-500">
Avatar-Farbe
</Typography>
<Select
label="Avatar-Farbe auswählen"
variant="outlined"
value={formValues.avatarColor}
onChange={handleAvatarColorChange}
disabled={avatarSelectDisabled}
selected={renderSelectedAvatarLabel}
>
<Option value="">
<span className="flex items-center gap-2">
<span className="h-4 w-4 rounded border border-blue-gray-100" />
<span className="text-xs">Keine</span>
</span>
</Option>
{AVATAR_COLORS.map((color) => (
<Option key={color} value={color}>
<span className="flex items-center gap-2">
<span className={`h-4 w-4 rounded border border-blue-gray-100 ${color}`} />
<span className="text-xs">{color}</span>
</span>
</Option>
))}
</Select>
</div>
</div>
<div>
<Typography variant="h6" color="blue-gray" className="mb-3">
Platform Settings
@@ -349,176 +613,10 @@ export function UserDetails() {
))}
</div>
</div>
<ProfileInfoCard
title="Profile Information"
description="Hi, I'm Alec Thompson, Decisions: If you can't decide, the answer is no. If two equally difficult paths, choose the one more painful in the short term (pain avoidance is creating an illusion of equality)."
details={{
"first name": "Alec M. Thompson",
mobile: "(44) 123 1234 123",
email: "alecthompson@mail.com",
location: "USA",
social: (
<div className="flex items-center gap-4">
<i className="fa-brands fa-facebook text-blue-700" />
<i className="fa-brands fa-twitter text-blue-400" />
<i className="fa-brands fa-instagram text-purple-500" />
</div>
),
}}
action={
<Tooltip content="Edit Profile">
<PencilIcon className="h-4 w-4 cursor-pointer text-blue-gray-500" />
</Tooltip>
}
/>
<div id="platform" className="flex flex-col gap-4">
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
<Typography variant="h6" color="blue-gray">
Globale Rolle
</Typography>
<div className="flex items-center gap-2">
<Button
variant="text"
size="sm"
color="blue-gray"
className="normal-case"
onClick={handleResetGroup}
disabled={!hasGroupChanges || savingGroup || !user}
>
Änderungen verwerfen
</Button>
<Button
size="sm"
color="green"
className="normal-case"
onClick={handleSaveGroup}
disabled={maintenanceActive || savingGroup || !hasGroupChanges || !user}
>
{savingGroup ? "Speichert ..." : "Speichern"}
</Button>
</div>
</div>
{maintenanceActive && (
<div className="rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
Wartungsmodus aktiv – Änderungen sind deaktiviert.
</div>
)}
{groupsError && (
<div className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800">
{groupsError}
</div>
)}
{groupsLoading && availableGroups.length === 0 ? (
<div className="flex items-center gap-3 rounded-lg border border-blue-gray-50 bg-blue-gray-50/50 px-4 py-3 text-sm text-blue-gray-500">
<Spinner className="h-4 w-4" />
<span>Benutzergruppen werden geladen ...</span>
</div>
) : availableGroups.length === 0 ? (
<Typography variant="small" className="text-sm text-stormGrey-500">
Es sind noch keine Benutzergruppen vorhanden.
</Typography>
) : (
<div className="flex flex-col gap-3 sm:flex-row sm:items-end">
<div className="sm:flex-1">
<Select
label="Benutzergruppe wählen"
value={selectValue}
onChange={handleGroupChange}
disabled={selectDisabled}
variant="outlined"
>
{availableGroups.map((group) => (
<Option key={group.id} value={String(group.id)}>
{group.name}
</Option>
))}
</Select>
</div>
</div>
)}
{!(groupsLoading && availableGroups.length === 0) && (
<Typography variant="small" className="text-sm text-stormGrey-500">
{selectedGroupLabel
? `Aktuell zugewiesene Gruppe: ${selectedGroupLabel}`
: "Dem Benutzer ist aktuell keine Gruppe zugewiesen."}
</Typography>
)}
</div>
</div>
<div className="px-4 pb-4">
<Typography variant="h6" color="blue-gray" className="mb-2">
Projects
</Typography>
<Typography
variant="small"
className="font-normal text-blue-gray-500"
>
Architects design houses
</Typography>
<div className="mt-6 grid grid-cols-1 gap-12 md:grid-cols-2 xl:grid-cols-4">
{projectsData.map(
({ img, title, description, tag, route, members }) => (
<Card key={title} color="transparent" shadow={false}>
<CardHeader
floated={false}
color="gray"
className="mx-0 mt-0 mb-4 h-64 xl:h-40"
>
<img
src={img}
alt={title}
className="h-full w-full object-cover"
/>
</CardHeader>
<CardBody className="py-0 px-1">
<Typography
variant="small"
className="font-normal text-blue-gray-500"
>
{tag}
</Typography>
<Typography
variant="h5"
color="blue-gray"
className="mt-1 mb-2"
>
{title}
</Typography>
<Typography
variant="small"
className="font-normal text-blue-gray-500"
>
{description}
</Typography>
</CardBody>
<CardFooter className="mt-6 flex items-center justify-between py-0 px-1">
<Link to={route}>
<Button variant="outlined" size="sm">
view project
</Button>
</Link>
<div>
{members.map(({ img, name }, key) => (
<Tooltip key={name} content={name}>
<Avatar
src={img}
alt={name}
size="xs"
variant="circular"
className={`cursor-pointer border-2 border-white ${key === 0 ? "" : "-ml-2.5"
}`}
/>
</Tooltip>
))}
</div>
</CardFooter>
</Card>
)
)}
</div>
</div>
</CardBody>
</Card>
</Card></div>
</>
);
}
@@ -0,0 +1,425 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import axios from "axios";
import { useParams } from "react-router-dom";
import {
Card,
CardBody,
Typography,
Button,
Spinner,
Select,
Option,
Input,
Chip
} from "@material-tailwind/react";
import { useToast } from "@/components/ToastProvider.jsx";
import { useMaintenance } from "@/components/MaintenanceProvider.jsx";
import { AVATAR_COLORS } from "@/data/avatarColors.js";
const _ = AVATAR_COLORS.join(" ");
const mapGroup = (item) => ({
id: item?.id ?? null,
name: item?.name || "",
description: item?.description || "",
avatarColor: item?.avatarColor || null,
createdAt: item?.createdAt || null,
updatedAt: item?.updatedAt || null,
memberCount: Number.isFinite(Number(item?.memberCount)) ? Number(item.memberCount) : 0,
members: Array.isArray(item?.members)
? item.members
.map((member) => ({
id: member?.id ?? null,
username: member?.username || ""
}))
.filter((member) => member.username)
: []
});
const buildInitialFormValues = (group) => {
if (!group) {
return {
name: "",
description: "",
avatarColor: ""
};
}
return {
name: group.name || "",
description: group.description || "",
avatarColor: group.avatarColor || ""
};
};
export function UserGroupDetail() {
const { groupId } = useParams();
const { showToast } = useToast();
const { maintenance } = useMaintenance();
const [group, setGroup] = useState(null);
const [formValues, setFormValues] = useState(buildInitialFormValues(null));
const initialFormValuesRef = useRef(buildInitialFormValues(null));
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const [hasLoaded, setHasLoaded] = useState(false);
const [savingGroup, setSavingGroup] = useState(false);
const [saveError, setSaveError] = useState("");
const maintenanceActive = Boolean(maintenance?.active);
const isSuperuserGroup = useMemo(() => (group?.name || "").toLowerCase() === "superuser", [group]);
const numericGroupId = useMemo(() => {
const candidate = Number(groupId);
return Number.isFinite(candidate) ? candidate : null;
}, [groupId]);
const fetchGroupDetails = useCallback(async () => {
if (!numericGroupId) {
setError("Ungültige Gruppen-ID.");
setGroup(null);
setFormValues(buildInitialFormValues(null));
initialFormValuesRef.current = buildInitialFormValues(null);
setHasLoaded(true);
return;
}
setLoading(true);
setError("");
try {
const response = await axios.get(`/api/groups/${numericGroupId}`);
const item = mapGroup(response.data?.item);
if (!item.id) {
throw new Error("GROUP_NOT_FOUND");
}
setGroup(item);
const initialValues = buildInitialFormValues(item);
initialFormValuesRef.current = { ...initialValues };
setFormValues(initialValues);
setSaveError("");
} catch (err) {
const serverError = err.response?.data?.error;
let message = "Gruppendetails konnten nicht geladen werden.";
if (serverError === "GROUP_NOT_FOUND") {
message = "Die angeforderte Benutzergruppe wurde nicht gefunden.";
} else if (serverError === "INVALID_GROUP_ID") {
message = "Die angegebene Gruppen-ID ist ungültig.";
} else if (err.response?.status === 404) {
message = "Die angeforderte Benutzergruppe existiert nicht.";
}
setGroup(null);
initialFormValuesRef.current = buildInitialFormValues(null);
setFormValues(buildInitialFormValues(null));
setError(message);
showToast({
variant: "error",
title: "Fehler beim Laden",
description: message
});
} finally {
setLoading(false);
setHasLoaded(true);
}
}, [numericGroupId, showToast]);
useEffect(() => {
fetchGroupDetails();
}, [fetchGroupDetails]);
const hasChanges = useMemo(() => {
if (!hasLoaded || !group) {
return false;
}
const initial = initialFormValuesRef.current;
if (!initial) {
return false;
}
const initialName = initial.name || "";
const currentName = formValues.name || "";
const initialDescription = initial.description || "";
const currentDescription = formValues.description || "";
const initialAvatar = initial.avatarColor || "";
const currentAvatar = formValues.avatarColor || "";
return (
initialName !== currentName ||
initialDescription !== currentDescription ||
initialAvatar !== currentAvatar
);
}, [formValues, hasLoaded, group]);
const handleNameChange = useCallback((event) => {
const { value } = event.target;
setFormValues((prev) => ({
...prev,
name: value
}));
}, []);
const handleDescriptionChange = useCallback((event) => {
const { value } = event.target;
setFormValues((prev) => ({
...prev,
description: value
}));
}, []);
const handleAvatarColorChange = useCallback((value) => {
setFormValues((prev) => ({
...prev,
avatarColor: value || ""
}));
}, []);
const handleSaveGroup = useCallback(async () => {
if (!group || !hasChanges) {
return;
}
setSavingGroup(true);
setSaveError("");
try {
const payload = {
avatarColor: formValues.avatarColor
};
if (!isSuperuserGroup) {
payload.name = formValues.name;
payload.description = formValues.description;
}
const response = await axios.put(`/api/groups/${group.id}`, payload);
const updated = mapGroup(response.data?.item || response.data?.group);
setGroup(updated);
const nextInitial = buildInitialFormValues(updated);
initialFormValuesRef.current = { ...nextInitial };
setFormValues(nextInitial);
showToast({
variant: "success",
title: "Gruppe gespeichert",
description: "Die Änderungen wurden erfolgreich gespeichert."
});
} catch (err) {
const serverError = err.response?.data?.error;
let message = "Die Gruppe konnte nicht gespeichert werden.";
if (serverError === "GROUP_NAME_REQUIRED") {
message = "Bitte einen Gruppennamen angeben.";
} else if (serverError === "GROUP_NAME_TAKEN") {
message = "Der Gruppenname wird bereits verwendet.";
} else if (serverError === "INVALID_AVATAR_COLOR") {
message = "Bitte eine gültige Avatar-Farbe auswählen.";
} else if (serverError === "GROUP_NOT_FOUND") {
message = "Die Benutzergruppe wurde nicht gefunden.";
} else if (serverError === "GROUP_SUPERUSER_PROTECTED") {
message = "Für die Superuser-Gruppe kann nur die Avatar-Farbe angepasst werden.";
}
setSaveError(message);
showToast({
variant: "error",
title: "Speichern fehlgeschlagen",
description: message
});
} finally {
setSavingGroup(false);
}
}, [group, hasChanges, formValues, showToast, isSuperuserGroup]);
const avatarLabel = useMemo(() => {
const source = (formValues.name || formValues.description || "").trim();
if (!source) {
return "?";
}
return source.charAt(0).toUpperCase();
}, [formValues.name, formValues.description]);
const avatarColorClass = useMemo(() => {
if (formValues.avatarColor) {
return formValues.avatarColor;
}
return group?.avatarColor || "";
}, [formValues.avatarColor, group]);
const selectDisabled = maintenanceActive || savingGroup || !group;
const inputDisabled = maintenanceActive || savingGroup || !group || isSuperuserGroup;
const selectValue = formValues.avatarColor || "";
return (
<>
<div className="mt-12 flex flex-col gap-12">
{maintenanceActive && (
<div className="mb-3 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
Wartungsmodus aktiv – Änderungen sind deaktiviert.
</div>
)}
<div className="relative h-72 w-full overflow-hidden rounded-xl bg-[url('/img/background-image.png')] bg-cover\tbg-center">
<div className="absolute inset-0 h-full w-full bg-gray-900/75" />
</div>
<Card className="mx-3 -mt-16 mb-6 lg:mx-4 border border-blue-gray-100">
<CardBody className="p-4">
<div className="mb-10 flex flex-wrap items-center justify-between gap-6">
<div className="flex items-center gap-6">
<div
className={`text-black flex h-[74px] w-[74px] items-center justify-center rounded-xl text-3xl font-semibold uppercase shadow-lg shadow-blue-gray-500/40 ${avatarColorClass}`}
aria-label={formValues.name || "Gruppenavatar"}
>
{avatarLabel}
</div>
<div>
<Typography variant="h5" color="blue-gray">
{formValues.name || "–"}
</Typography>
<Typography className="text-xs font-semibold tracking-wide text-stormGrey-400">
Gruppen-ID: {group?.id ?? "–"}
</Typography>
</div>
</div>
{hasChanges && (
<Button
color="green"
className="normal-case"
onClick={handleSaveGroup}
disabled={maintenanceActive || savingGroup}
>
{savingGroup ? "Speichert ..." : "Änderungen speichern"}
</Button>
)}
</div>
{loading && !group && (
<div className="mb-6 flex items-center gap-3 rounded-lg border border-blue-gray-50 bg-blue-gray-50/50 px-4 py-3 text-sm text-blue-gray-500">
<Spinner className="h-4 w-4" />
<span>Gruppendaten werden geladen ...</span>
</div>
)}
{error && !loading && (
<div className="mb-6 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800">
{error}
</div>
)}
{saveError && !loading && (
<div className="mb-6 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800">
{saveError}
</div>
)}
<div className="grid-cols-1 mb-12 grid gap-12 px-4 lg:grid-cols-2 xl:grid-cols-3">
<div>
<Typography variant="h6" color="blue-gray" className="mb-4">
Gruppendaten
</Typography>
{(maintenanceActive || isSuperuserGroup) && (
<div className="mb-4 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
{maintenanceActive
? "Wartungsmodus aktiv – Änderungen sind deaktiviert."
: "Systemgruppe – Name und Beschreibung sind geschützt."}
</div>
)}
<div className="mb-6">
<Typography className="mb-2 block text-xs font-semibold uppercase text-blue-gray-500">
Gruppenname
</Typography>
<Input
value={formValues.name}
onChange={handleNameChange}
placeholder="Gruppenname"
disabled={inputDisabled}
className=" !border-t-blue-gray-200 focus:!border-t-gray-900"
labelProps={{
className: "before:content-none after:content-none"
}}
/>
</div>
<div className="mb-6">
<Typography className="mb-2 block text-xs font-semibold uppercase text-blue-gray-500">
Beschreibung
</Typography>
<Input
value={formValues.description}
onChange={handleDescriptionChange}
placeholder="Beschreibung der Gruppe"
disabled={inputDisabled}
className=" !border-t-blue-gray-200 focus:!border-t-gray-900"
labelProps={{
className: "before:content-none after:content-none"
}}
/>
</div>
<div className="mb-6">
<Typography className="mb-2 block text-xs font-semibold uppercase text-blue-gray-500">
Avatar-Farbe
</Typography>
<Select
label="Avatar-Farbe auswählen"
variant="outlined"
value={selectValue}
onChange={handleAvatarColorChange}
disabled={selectDisabled}
selected={(element) => {
if (element?.props?.children) {
return element.props.children;
}
if (!formValues.avatarColor) {
return "Standardfarbe";
}
return formValues.avatarColor;
}}
>
<Option value="">
<span className="flex items-center gap-2">
<span className="h-4 w-4 rounded border border-blue-gray-100" />
<span className="text-xs">Keine</span>
</span>
</Option>
{AVATAR_COLORS.map((color) => (
<Option key={color} value={color}>
<span className="flex items-center gap-2">
<span className={`h-4 w-4 rounded border border-blue-gray-100 ${color}`} />
<span className="text-xs">{color}</span>
</span>
</Option>
))}
</Select>
</div>
</div>
<div>
<Typography variant="h6" color="blue-gray" className="mb-4">
Mitglieder
</Typography>
{!group || group.members.length === 0 ? (
<Typography className="text-sm text-stormGrey-500">
Aktuell sind keine Benutzer dieser Gruppe zugeordnet.
</Typography>
) : (
<div className="flex flex-wrap gap-2">
{group.members.map((member) => (
<Chip
key={`${group.id}-${member.id}-${member.username}`}
value={member.username}
size="sm"
color="blue-gray"
variant="ghost"
/>
))}
</div>
)}
<div className="mt-6 text-xs text-stormGrey-400">
<p>Mitglieder insgesamt: {group?.memberCount ?? 0}</p>
</div>
</div>
</div>
</CardBody>
</Card></div>
</>
);
}
export default UserGroupDetail;
+116 -35
View File
@@ -1,5 +1,6 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import {
Card,
CardHeader,
@@ -24,10 +25,12 @@ export function Usergroups() {
const [newGroupDescription, setNewGroupDescription] = useState("");
const [createGroupError, setCreateGroupError] = useState("");
const [creatingGroup, setCreatingGroup] = useState(false);
const [deletingGroupId, setDeletingGroupId] = useState(null);
const { showToast } = useToast();
const { maintenance } = useMaintenance();
const maintenanceActive = Boolean(maintenance?.active);
const navigate = useNavigate();
const {
page,
@@ -54,6 +57,7 @@ export function Usergroups() {
id: item.id,
name: item.name || "",
description: item.description || "",
avatarColor: item.avatarColor || null,
memberCount: Number(item.memberCount) || 0,
members: Array.isArray(item.members)
? item.members.map((member) => ({
@@ -233,8 +237,86 @@ export function Usergroups() {
const createGroupDisabled = maintenanceActive || creatingGroup;
const handleDeleteGroup = useCallback(async (group) => {
if (maintenanceActive || !group?.id) {
return;
}
const isSuperuserGroup = (group?.name || "").toLowerCase() === "superuser";
if (isSuperuserGroup) {
showToast({
variant: "error",
title: "Löschen nicht möglich",
description: "Die Superuser-Gruppe kann nicht gelöscht werden."
});
return;
}
const memberCount = Number(group.memberCount) || 0;
if (memberCount > 0) {
showToast({
variant: "error",
title: "Löschen nicht möglich",
description: "Die Gruppe enthält noch Benutzer. Bitte entfernen Sie diese zuerst."
});
return;
}
const confirmation = window.confirm(`Benutzergruppe "${group.name}" wirklich löschen?`);
if (!confirmation) {
return;
}
const numericId = Number(group.id);
if (!Number.isFinite(numericId) || numericId <= 0) {
showToast({
variant: "error",
title: "Löschen fehlgeschlagen",
description: "Ungültige Gruppen-ID."
});
return;
}
setDeletingGroupId(numericId);
try {
await axios.delete(`/api/groups/${numericId}`);
showToast({
variant: "success",
title: "Gruppe gelöscht",
description: `Die Benutzergruppe "${group.name}" wurde entfernt.`
});
fetchGroups();
} catch (err) {
const serverError = err.response?.data?.error;
let message = "Die Benutzergruppe konnte nicht gelöscht werden.";
if (serverError === "INVALID_GROUP_ID") {
message = "Die Gruppen-ID ist ungültig.";
} else if (serverError === "GROUP_NOT_FOUND") {
message = "Die Benutzergruppe wurde bereits entfernt.";
} else if (serverError === "GROUP_HAS_MEMBERS") {
message = "Die Gruppe enthält noch Benutzer. Bitte entfernen Sie diese zuerst.";
} else if (serverError === "GROUP_SUPERUSER_PROTECTED") {
message = "Die Superuser-Gruppe kann nicht gelöscht werden.";
}
showToast({
variant: "error",
title: "Löschen fehlgeschlagen",
description: message
});
} finally {
setDeletingGroupId(null);
}
}, [maintenanceActive, showToast, fetchGroups]);
return (
<div className="mt-12">
<div className="mt-12 mb-8 flex flex-col gap-12">
{maintenanceActive && (
<div className="mb-4 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
Wartungsmodus aktiv – Änderungen sind deaktiviert. Die Liste kann dennoch angezeigt werden.
</div>
)}
<Card className="border border-blue-gray-100 shadow-sm">
<CardHeader variant="gradient" color="gray" className="mb-5 p-4">
<Typography
@@ -245,13 +327,6 @@ export function Usergroups() {
</Typography>
</CardHeader>
<CardBody className="pt-0">
{maintenanceActive && (
<div className="mb-4 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
Wartungsmodus aktiv – Änderungen sind deaktiviert. Die Liste kann dennoch angezeigt werden.
</div>
)}
<div className="mb-8 rounded-lg border border-blue-gray-100 bg-white p-4 shadow-sm">
<Typography variant="h6" color="blue-gray" className="mb-2">
Neue Benutzergruppe anlegen
@@ -335,7 +410,6 @@ export function Usergroups() {
<th className="px-6 py-4 font-semibold">Gruppenname</th>
<th className="px-6 py-4 font-semibold">Beschreibung</th>
<th className="px-6 py-4 font-semibold">Mitglieder</th>
<th className="px-6 py-4 font-semibold">Anzahl</th>
<th className="px-6 py-4 font-semibold">Erstellt am</th>
<th className="px-6 py-4 font-semibold">Zuletzt aktualisiert</th>
<th className="px-6 py-4 font-semibold">Aktionen</th>
@@ -358,6 +432,7 @@ export function Usergroups() {
) : (
paginatedGroups.map((group, index) => {
const rowClass = index === paginatedGroups.length - 1 ? "" : "border-b border-blue-gray-50";
const isSuperuserGroup = (group?.name || "").toLowerCase() === "superuser";
return (
<tr key={group.id} className={`text-sm text-stormGrey-700 ${rowClass}`}>
<td className="px-6 py-4">
@@ -375,29 +450,10 @@ export function Usergroups() {
</Typography>
</td>
<td className="px-6 py-4">
{group.members.length > 0 ? (
<div className="flex flex-wrap gap-2">
{group.members.map((member) => (
<Chip
key={`${group.id}-${member.id}-${member.username}`}
value={member.username}
size="sm"
color="blue-gray"
variant="ghost"
/>
))}
</div>
) : (
<span className="text-stormGrey-400">Keine Mitglieder</span>
)}
</td>
<td className="px-6 py-4">
<Chip
value={group.memberCount.toLocaleString("de-DE")}
size="sm"
color={group.memberCount > 0 ? "green" : "blue-gray"}
variant="ghost"
/>
<Typography variant="small" className="antialiased font-sans mb-1 block text-xs font-medium text-stormGrey-600">
{group.memberCount.toLocaleString("de-DE")}
</Typography>
</td>
<td className="px-6 py-4">
<Typography variant="small" className="antialiased font-sans mb-1 block text-xs font-medium text-stormGrey-600">
@@ -410,9 +466,34 @@ export function Usergroups() {
</Typography>
</td>
<td className="px-6 py-4">
<Typography variant="small" className="antialiased font-sans mb-1 block text-xs font-medium text-stormGrey-600">
Aktionen
</Typography>
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-2">
<Button
size="sm"
variant="outlined"
color="blue-gray"
onClick={() => navigate(`/dashboard/usergroups/${group.id}`)}
>
Details
</Button>
<Button
size="sm"
variant="text"
color="red"
onClick={() => handleDeleteGroup(group)}
disabled={
maintenanceActive ||
deletingGroupId === group.id ||
Number(group.memberCount) > 0 ||
isSuperuserGroup
}
>
{isSuperuserGroup
? ""
: deletingGroupId === group.id
? "Löscht ..."
: "Löschen"}
</Button>
</div>
</td>
</tr>
);
+179 -106
View File
@@ -15,7 +15,6 @@ import { useNavigate } from "react-router-dom";
import { PaginationControls, usePage } from "@/components/PageProvider.jsx";
import { useMaintenance } from "@/components/MaintenanceProvider.jsx";
import { useToast } from "@/components/ToastProvider.jsx";
import { AVATAR_COLORS } from "@/data/avatarColors.js";
const normalizeUserGroups = (rawGroups) => {
if (!Array.isArray(rawGroups)) {
@@ -55,9 +54,10 @@ export function Users() {
const [newEmail, setNewEmail] = useState("");
const [newPassword, setNewPassword] = useState("");
const [newGroupId, setNewGroupId] = useState("");
const [newAvatarColor, setNewAvatarColor] = useState("");
const [createError, setCreateError] = useState("");
const [creatingUser, setCreatingUser] = useState(false);
const [deletingUserId, setDeletingUserId] = useState(null);
const [togglingUserId, setTogglingUserId] = useState(null);
const { showToast } = useToast();
const { maintenance } = useMaintenance();
@@ -123,6 +123,7 @@ export function Users() {
name: item.name || ""
}))
.filter((group) => Number.isFinite(group.id) && group.id > 0 && group.name)
.filter((group) => group.name.toLowerCase() !== "superuser")
.sort((a, b) => a.name.localeCompare(b.name, "de-DE"));
setAvailableGroups(normalized);
} catch (err) {
@@ -243,10 +244,140 @@ export function Users() {
setNewEmail("");
setNewPassword("");
setNewGroupId("");
setNewAvatarColor("");
setCreateError("");
}, []);
const handleDeleteUser = useCallback(async (user) => {
if (maintenanceActive || !user?.id) {
return;
}
const isSuperuserUser = Array.isArray(user?.groups)
? user.groups.some((group) => (group?.name || "").toLowerCase() === "superuser")
: false;
if (isSuperuserUser) {
showToast({
variant: "error",
title: "Löschen nicht möglich",
description: "Der Superuser kann nicht gelöscht werden."
});
return;
}
const confirmation = window.confirm(`Benutzer "${user.username}" wirklich löschen?`);
if (!confirmation) {
return;
}
const numericId = Number(user.id);
if (!Number.isFinite(numericId) || numericId <= 0) {
showToast({
variant: "error",
title: "Löschen fehlgeschlagen",
description: "Ungültige Benutzer-ID."
});
return;
}
setDeletingUserId(numericId);
try {
await axios.delete(`/api/users/${numericId}`);
showToast({
variant: "success",
title: "Benutzer gelöscht",
description: `Der Benutzer "${user.username}" wurde entfernt.`
});
fetchUsers();
} catch (err) {
const serverError = err.response?.data?.error;
let message = "Der Benutzer konnte nicht gelöscht werden.";
if (serverError === "INVALID_USER_ID") {
message = "Die Benutzer-ID ist ungültig.";
} else if (serverError === "USER_NOT_FOUND") {
message = "Der Benutzer wurde bereits entfernt.";
} else if (serverError === "USER_SUPERUSER_PROTECTED") {
message = "Der Superuser kann nicht gelöscht werden.";
}
showToast({
variant: "error",
title: "Löschen fehlgeschlagen",
description: message
});
} finally {
setDeletingUserId(null);
}
}, [maintenanceActive, showToast, fetchUsers]);
const handleToggleUserStatus = useCallback(async (user) => {
if (!user?.id) {
return;
}
if (maintenanceActive) {
showToast({
variant: "error",
title: "Aktion nicht möglich",
description: "Im Wartungsmodus können keine Statusänderungen durchgeführt werden."
});
return;
}
const isSuperuserUser = Array.isArray(user?.groups)
? user.groups.some((group) => (group?.name || "").toLowerCase() === "superuser")
: false;
if (isSuperuserUser && user.isActive) {
showToast({
variant: "error",
title: "Änderung nicht möglich",
description: "Der Superuser kann nicht deaktiviert werden."
});
return;
}
const numericId = Number(user.id);
if (!Number.isFinite(numericId) || numericId <= 0) {
showToast({
variant: "error",
title: "Statusänderung fehlgeschlagen",
description: "Ungültige Benutzer-ID."
});
return;
}
const nextActive = !user.isActive;
setTogglingUserId(numericId);
try {
await axios.put(`/api/users/${numericId}/active`, { isActive: nextActive });
fetchUsers();
showToast({
variant: "success",
title: "Status aktualisiert",
description: `Der Benutzer wurde ${nextActive ? "aktiviert" : "deaktiviert"}.`
});
} catch (err) {
const serverError = err.response?.data?.error;
let message = "Der Benutzerstatus konnte nicht geändert werden.";
if (serverError === "INVALID_USER_ID") {
message = "Die Benutzer-ID ist ungültig.";
} else if (serverError === "USER_NOT_FOUND") {
message = "Der Benutzer wurde nicht gefunden.";
} else if (serverError === "USER_SUPERUSER_PROTECTED") {
message = "Der Superuser kann nicht deaktiviert werden.";
}
showToast({
variant: "error",
title: "Statusänderung fehlgeschlagen",
description: message
});
} finally {
setTogglingUserId(null);
}
}, [maintenanceActive, showToast, fetchUsers]);
const handleCreateUser = useCallback(async () => {
if (maintenanceActive) {
return;
@@ -277,9 +408,6 @@ export function Users() {
if (trimmedEmail) {
payload.email = trimmedEmail;
}
if (newAvatarColor) {
payload.avatarColor = newAvatarColor;
}
const response = await axios.post("/api/users", payload);
resetNewUserForm();
fetchUsers();
@@ -330,16 +458,14 @@ export function Users() {
newEmail,
newPassword,
newGroupId,
newAvatarColor,
showToast,
resetNewUserForm,
fetchUsers
]);
const createDisabled = maintenanceActive || creatingUser || groupsLoading || availableGroups.length === 0;
const groupSelectDisabled = maintenanceActive || creatingUser || groupsLoading || availableGroups.length === 0;
const avatarSelectDisabled = maintenanceActive || creatingUser;
const hasSelectableGroups = availableGroups.length > 0;
const createDisabled = maintenanceActive || creatingUser || groupsLoading || !hasSelectableGroups;
const groupSelectDisabled = maintenanceActive || creatingUser || groupsLoading || !hasSelectableGroups;
const renderSelectedGroup = useCallback(
(element) => {
if (element && element.props && element.props.children) {
@@ -354,26 +480,13 @@ export function Users() {
[availableGroups, newGroupId]
);
const renderSelectedAvatar = useCallback(
(element) => {
if (element && element.props && element.props.children) {
return element.props.children;
}
if (!newAvatarColor) {
return "Automatisch (zufällig)";
}
return (
<span className="flex items-center gap-2">
<span className={`h-4 w-4 rounded border border-blue-gray-100 ${newAvatarColor}`} />
<span className="text-xs">{newAvatarColor}</span>
</span>
);
},
[newAvatarColor]
);
return (
<div className="mt-12">
<div className="mt-12 mb-8 flex flex-col gap-12">
{maintenanceActive && (
<div className="mb-4 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
Wartungsmodus aktiv – Änderungen sind deaktiviert. Die Liste kann dennoch angezeigt werden.
</div>
)}
<Card>
<CardHeader variant="gradient" color="gray" className="mb-5 p-4">
<Typography
@@ -384,16 +497,14 @@ export function Users() {
</Typography>
</CardHeader>
<CardBody className="pt-0">
{maintenanceActive && (
<div className="mb-4 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
Wartungsmodus aktiv – Änderungen sind deaktiviert. Die Liste kann dennoch angezeigt werden.
</div>
)}
<div className="mb-8 rounded-lg border border-blue-gray-100 bg-white p-4 shadow-sm">
<Typography variant="h6" color="blue-gray" className="mb-2">
Neuen Benutzer anlegen
</Typography>
{hasSelectableGroups ? (
<>
<Typography variant="small" className="text-sm text-stormGrey-500 mb-4">
Benutzername, Passwort (mindestens 8 Zeichen) und eine globale Rolle sind Pflichtfelder.
</Typography>
@@ -445,30 +556,10 @@ export function Users() {
</Option>
))}
</Select>
<Select
label="Avatar-Farbe"
variant="outlined"
value={newAvatarColor}
onChange={setNewAvatarColor}
disabled={avatarSelectDisabled}
selected={renderSelectedAvatar}
>
<Option value="">Automatisch (zufällig)</Option>
{AVATAR_COLORS.map((color) => (
<Option key={color} value={color}>
<span className="flex items-center gap-2">
<span className={`h-4 w-4 rounded border border-blue-gray-100 ${color}`} />
<span className="text-xs">{color}</span>
</span>
</Option>
))}
</Select>
</div>
<div className="mt-4 text-sm text-stormGrey-500">
{groupsLoading
? "Benutzergruppen werden geladen ..."
: availableGroups.length === 0
? "Noch keine Benutzergruppen vorhanden. Bitte legen Sie zuerst eine globale Rolle an."
: ""}
</div>
<div className="mt-4 flex flex-col gap-3 sm:flex-row sm:items-center">
@@ -479,58 +570,15 @@ export function Users() {
Formular zurücksetzen
</Button>
</div>
</>
) : (
<div className="rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
Es existiert keine verfügbare Benutzergruppe. Bitte lege zuerst eine Gruppe an.
</div>
)}
</div>
<div className="mb-8">
<div className="flex flex-wrap gap-2"> {/* <div className="flex flex-col items-stretch gap-3 md:flex-row md:items-center">
<div>
<label className="mb-1 block text-xs font-semibold uppercase tracking-wide text-blue-gray-400">
Einträge pro Seite
</label>
<select
value={perPage}
onChange={handlePerPageChange}
className="w-full rounded-lg border border-blue-gray-100 px-3 py-2 text-sm text-blue-gray-700 shadow-sm focus:border-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-200 md:w-40"
>
{perPageOptions.map(({ value, label }) => (
<option key={value} value={value}>
{label}
</option>
))}
</select>
</div>
<Button
variant="outlined"
color="blue"
onClick={handleRefresh}
disabled={loading}
className="whitespace-nowrap"
>
{loading ? "Lädt ..." : "Aktualisieren"}
</Button>
</div> */}
{/* <div className="w-full md:max-w-md">
<Input
label="Suchen nach Name, E-Mail oder Gruppe"
value={searchQuery}
onChange={handleSearchChange}
disabled={loading && !users.length}
crossOrigin=""
/>
</div> */}
{/* {searchQuery && (
<Button
variant="text"
color="blue-gray"
onClick={handleClearSearch}
className="w-full md:w-auto"
>
Suche zurücksetzen
</Button>
)} */}
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between mt-8">
<div className="md:flex-1">
<Input
@@ -557,7 +605,6 @@ export function Users() {
</div>
</div>
</div>
</div>
{error && (
<div className="mb-4 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800">
{error}
@@ -594,6 +641,9 @@ export function Users() {
) : (
paginatedUsers.map((user, index) => {
const rowClass = index === paginatedUsers.length - 1 ? "" : "border-b border-blue-gray-50";
const isSuperuserUser = Array.isArray(user?.groups)
? user.groups.some((group) => (group?.name || "").toLowerCase() === "superuser")
: false;
return (
<tr key={user.id} className={`text-sm text-stormGrey-700 ${rowClass}`}>
<td className="px-6 py-4">
@@ -624,12 +674,20 @@ export function Users() {
)}
</td>
<td className="px-6 py-4">
<button
type="button"
className={`inline-flex items-center rounded-full px-1 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 ${maintenanceActive ? "cursor-not-allowed" : "cursor-pointer"}`}
onClick={() => handleToggleUserStatus(user)}
disabled={maintenanceActive || togglingUserId === user.id || (Array.isArray(user.groups) && user.groups.some((group) => (group?.name || "").toLowerCase() === "superuser" && user.isActive))}
>
<Chip
value={user.isActive ? "Aktiv" : "Deaktiviert"}
value={togglingUserId === user.id ? "Wechselt ..." : user.isActive ? "Aktiv" : "Deaktiviert"}
size="sm"
color={user.isActive ? "green" : "red"}
variant="ghost"
className="pointer-events-none"
/>
</button>
</td>
<td className="px-6 py-4">
<Typography variant="small" className="antialiased font-sans mb-1 block text-xs font-medium text-stormGrey-600">
@@ -642,6 +700,7 @@ export function Users() {
</Typography>
</td>
<td className="px-6 py-4">
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-2">
<Button
size="sm"
variant="outlined"
@@ -650,6 +709,20 @@ export function Users() {
>
Details
</Button>
<Button
size="sm"
variant="text"
color="red"
onClick={() => handleDeleteUser(user)}
disabled={maintenanceActive || deletingUserId === user.id || isSuperuserUser}
>
{isSuperuserUser
? ""
: deletingUserId === user.id
? "Löscht ..."
: "Löschen"}
</Button>
</div>
</td>
</tr>
);