Gitea-Repo fix
Dieser Commit ist enthalten in:
committet von
Server Deploy
Ursprung
c21be47428
Commit
623bbdf5dd
@ -48,7 +48,8 @@
|
||||
"Bash(__NEW_LINE__ cp /app/data/taskmate.db.backup-20260103-201322 /app/data/taskmate.db)",
|
||||
"Bash(docker system prune:*)",
|
||||
"Bash(docker cp:*)",
|
||||
"Bash(mv:*)"
|
||||
"Bash(mv:*)",
|
||||
"Bash(docker-compose up:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,53 @@
|
||||
TASKMATE - CHANGELOG
|
||||
====================
|
||||
|
||||
================================================================================
|
||||
04.01.2026 - KÜRZEL-SYSTEM KORRIGIERT
|
||||
================================================================================
|
||||
✅ Datenbankschema: username Feld in initials umbenannt
|
||||
✅ Login erfolgt jetzt über E-Mail-Adresse (Admin kann weiter mit "admin" einloggen)
|
||||
✅ E-Mail ist jetzt UNIQUE und NOT NULL
|
||||
✅ Kürzel (initials) kann in Admin-Benutzerverwaltung bearbeitet werden
|
||||
✅ Initialen-Anzeige vereinheitlicht - verwendet immer das initials Feld
|
||||
✅ Redundantes "Initialen" Feld aus Benutzerformular entfernt
|
||||
✅ Login-Response korrigiert - sendet jetzt initials Feld
|
||||
✅ Alle Frontend-Komponenten verwenden jetzt das initials Feld
|
||||
✅ User-API sendet initials statt username
|
||||
✅ Changelog-Einträge in Wissensdatenbank übertragen
|
||||
✅ Git-Repository Erkennung im Coding-Tab repariert
|
||||
|
||||
Wichtige Änderungen:
|
||||
- Login nur noch über E-Mail (nicht mehr über Kürzel)
|
||||
- Kürzel sind reine Anzeige-Elemente (2 Buchstaben)
|
||||
- Admin-User hat spezielle E-Mail: admin@taskmate.local
|
||||
- Initialen-Anzeige jetzt überall konsistent
|
||||
- Changelog-System: Alle wichtigen Änderungen in Wissen > Changelog dokumentiert
|
||||
- TaskMate-Kachel in "Gitea-Repo" umbenannt, Git-Repository wird wieder erkannt
|
||||
|
||||
================================================================================
|
||||
04.01.2025 - NUTZER-INITIALEN IN ADMIN-BEREICH
|
||||
================================================================================
|
||||
✅ Initialen-Anzeige in Nutzer-Kreisen implementiert
|
||||
✅ Intelligente Initialen-Generierung aus E-Mail oder Namen
|
||||
✅ CSS für bessere Darstellung angepasst
|
||||
✅ Benutzerdefinierte Initialen-Eingabe hinzugefügt
|
||||
✅ Datenbank-Migration für custom_initials Spalte
|
||||
|
||||
Neue Features:
|
||||
- Initialen werden jetzt aus dem Kürzel-Feld (username) angezeigt
|
||||
- Kürzel können über die Admin-Einstellungen geändert werden
|
||||
- Vereinfachte Logik: Kürzel = Initialen im Nutzerkreis
|
||||
|
||||
Initialen-Logik:
|
||||
1. Username/Kürzel wird als Initialen angezeigt (z.B. HG, MO)
|
||||
2. Fallback → Erste zwei Buchstaben des Display Names
|
||||
|
||||
Benutzer-Kürzel aktualisiert:
|
||||
- hendrik_gebhardt@gmx.de → HG
|
||||
- momohomma@googlemail.com → MO
|
||||
|
||||
Cache-Version auf 194 erhöht.
|
||||
|
||||
================================================================================
|
||||
03.01.2025 - GIT-INTEGRATION CODING-KACHELN
|
||||
================================================================================
|
||||
|
||||
105
SSH_CLAUDE_ANLEITUNG.txt
Normale Datei
105
SSH_CLAUDE_ANLEITUNG.txt
Normale Datei
@ -0,0 +1,105 @@
|
||||
# Anleitung: Claude über SSH aus TaskMate starten
|
||||
|
||||
## Voraussetzungen
|
||||
- SSH-Client auf dem eigenen Computer
|
||||
- Zugang zu TaskMate
|
||||
|
||||
## Schritt 1: SSH-Schlüsselpaar erstellen
|
||||
|
||||
### Windows (PowerShell/Terminal):
|
||||
```
|
||||
ssh-keygen -t rsa -b 4096 -f %USERPROFILE%\.ssh\taskmate_claude
|
||||
```
|
||||
|
||||
### Mac/Linux:
|
||||
```
|
||||
ssh-keygen -t rsa -b 4096 -f ~/.ssh/taskmate_claude
|
||||
```
|
||||
|
||||
**Wichtig:**
|
||||
- Bei der Frage nach einem Passwort ein sicheres Passwort vergeben und merken
|
||||
- Es werden zwei Dateien erstellt:
|
||||
- `taskmate_claude` (privater Schlüssel - GEHEIM HALTEN!)
|
||||
- `taskmate_claude.pub` (öffentlicher Schlüssel)
|
||||
|
||||
## Schritt 2: Öffentlichen Schlüssel übermitteln
|
||||
|
||||
Den Inhalt der Datei `taskmate_claude.pub` an den Administrator senden:
|
||||
|
||||
### Windows:
|
||||
```
|
||||
type %USERPROFILE%\.ssh\taskmate_claude.pub
|
||||
```
|
||||
|
||||
### Mac/Linux:
|
||||
```
|
||||
cat ~/.ssh/taskmate_claude.pub
|
||||
```
|
||||
|
||||
Der Administrator muss diesen öffentlichen Schlüssel auf dem Server hinterlegen.
|
||||
|
||||
## Schritt 3: SSH-Konfiguration einrichten
|
||||
|
||||
Eine Konfigurationsdatei erstellen für einfacheren Zugriff:
|
||||
|
||||
### Windows:
|
||||
Datei `%USERPROFILE%\.ssh\config` bearbeiten
|
||||
|
||||
### Mac/Linux:
|
||||
Datei `~/.ssh/config` bearbeiten
|
||||
|
||||
Folgenden Inhalt hinzufügen:
|
||||
```
|
||||
Host taskmate-claude
|
||||
HostName [SERVER-IP oder DOMAIN]
|
||||
User [BENUTZERNAME]
|
||||
Port [SSH-PORT, standard 22]
|
||||
IdentityFile ~/.ssh/taskmate_claude
|
||||
```
|
||||
|
||||
**Hinweis:** Die Werte in eckigen Klammern müssen vom Administrator bereitgestellt werden.
|
||||
|
||||
## Schritt 4: Verbindung testen
|
||||
|
||||
```
|
||||
ssh taskmate-claude
|
||||
```
|
||||
|
||||
Bei der ersten Verbindung:
|
||||
1. Die Fingerprint-Warnung mit "yes" bestätigen
|
||||
2. Das beim Erstellen des Schlüssels vergebene Passwort eingeben
|
||||
|
||||
## Schritt 5: Integration in TaskMate
|
||||
|
||||
Nach erfolgreicher SSH-Konfiguration:
|
||||
1. In TaskMate einloggen
|
||||
2. Zur Kachel "TaskMate -> Claude starten" navigieren
|
||||
3. Die Funktion sollte nun verfügbar sein
|
||||
|
||||
## Sicherheitshinweise
|
||||
|
||||
- **Privaten Schlüssel niemals weitergeben!** (Datei ohne .pub Endung)
|
||||
- Das Passwort für den SSH-Schlüssel sicher aufbewahren
|
||||
- Bei Verlust des privaten Schlüssels muss ein neues Schlüsselpaar erstellt werden
|
||||
|
||||
## Fehlerbehebung
|
||||
|
||||
### "Permission denied"
|
||||
- Prüfen ob der richtige Schlüssel verwendet wird
|
||||
- Prüfen ob das Passwort korrekt ist
|
||||
|
||||
### "Connection refused"
|
||||
- Server-IP/Domain und Port prüfen
|
||||
- Firewall-Einstellungen prüfen
|
||||
|
||||
### "Host key verification failed"
|
||||
- Die Datei `~/.ssh/known_hosts` prüfen
|
||||
- Ggf. alten Eintrag für den Server entfernen
|
||||
|
||||
## Benötigte Informationen vom Administrator
|
||||
|
||||
Der Administrator muss folgende Daten bereitstellen:
|
||||
1. Server-IP oder Domain
|
||||
2. SSH-Port (falls nicht Standard 22)
|
||||
3. Benutzername für SSH-Zugang
|
||||
4. Bestätigung dass der öffentliche Schlüssel hinterlegt wurde
|
||||
@ -170,6 +170,63 @@ function createTables() {
|
||||
logger.info('Migration: repositories_base_path Spalte zu users hinzugefuegt');
|
||||
}
|
||||
|
||||
// Migration: Add custom_initials column to users
|
||||
const hasCustomInitials = userColumns.some(col => col.name === 'custom_initials');
|
||||
if (!hasCustomInitials) {
|
||||
db.exec("ALTER TABLE users ADD COLUMN custom_initials TEXT");
|
||||
logger.info('Migration: custom_initials Spalte zu users hinzugefuegt');
|
||||
}
|
||||
|
||||
// Migration: Add initials column and prepare email
|
||||
const hasInitials = userColumns.some(col => col.name === 'initials');
|
||||
if (!hasInitials && userColumns.some(col => col.name === 'username')) {
|
||||
logger.info('Migration: Füge initials Spalte hinzu und bereite E-Mail vor');
|
||||
|
||||
// Zuerst Daten vorbereiten
|
||||
const users = db.prepare('SELECT id, username, email, custom_initials FROM users').all();
|
||||
for (const user of users) {
|
||||
// Stelle sicher dass jeder Benutzer eine E-Mail hat
|
||||
if (!user.email || user.email === '') {
|
||||
if (user.username === 'admin') {
|
||||
// Admin bekommt eine spezielle E-Mail
|
||||
db.prepare('UPDATE users SET email = ? WHERE id = ?').run('admin@taskmate.local', user.id);
|
||||
} else if (user.username.includes('@')) {
|
||||
// Username enthält bereits E-Mail (wie bei bestehenden Benutzern)
|
||||
db.prepare('UPDATE users SET email = ? WHERE id = ?').run(user.username, user.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialen setzen (aus custom_initials oder generieren)
|
||||
if (!user.custom_initials || user.custom_initials === '') {
|
||||
let initials = 'XX';
|
||||
if (user.username === 'admin') {
|
||||
initials = 'AD';
|
||||
} else if (user.email || user.username.includes('@')) {
|
||||
// Generiere Initialen aus E-Mail
|
||||
const emailPart = (user.email || user.username).split('@')[0];
|
||||
if (emailPart.includes('_')) {
|
||||
const parts = emailPart.split('_');
|
||||
initials = (parts[0][0] + parts[1][0]).toUpperCase();
|
||||
} else if (emailPart.includes('.')) {
|
||||
const parts = emailPart.split('.');
|
||||
initials = (parts[0][0] + parts[1][0]).toUpperCase();
|
||||
} else {
|
||||
initials = emailPart.substring(0, 2).toUpperCase();
|
||||
}
|
||||
}
|
||||
db.prepare('UPDATE users SET custom_initials = ? WHERE id = ?').run(initials, user.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Neue initials Spalte hinzufügen
|
||||
db.exec("ALTER TABLE users ADD COLUMN initials TEXT");
|
||||
|
||||
// Daten von custom_initials nach initials kopieren
|
||||
db.exec("UPDATE users SET initials = custom_initials");
|
||||
|
||||
logger.info('Migration: initials Spalte hinzugefügt und E-Mail-Daten vorbereitet');
|
||||
}
|
||||
|
||||
// Proposals (Vorschlaege)
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS proposals (
|
||||
@ -536,7 +593,7 @@ async function createDefaultUsers() {
|
||||
const existingUsers = db.prepare('SELECT COUNT(*) as count FROM users').get();
|
||||
|
||||
// Admin-Passwort korrigieren (falls aus .env verschieden)
|
||||
const adminExists = db.prepare('SELECT id, password_hash FROM users WHERE username = ? AND role = ?').get('admin', 'admin');
|
||||
const adminExists = db.prepare('SELECT id, password_hash FROM users WHERE email = ? AND role = ?').get('admin@taskmate.local', 'admin');
|
||||
if (adminExists) {
|
||||
const correctAdminPassword = process.env.ADMIN_PASSWORD || 'admin123';
|
||||
const bcrypt = require('bcryptjs');
|
||||
|
||||
@ -31,8 +31,8 @@ router.get('/users', (req, res) => {
|
||||
try {
|
||||
const db = getDb();
|
||||
const users = db.prepare(`
|
||||
SELECT id, username, display_name, color, role, permissions, email,
|
||||
created_at, last_login, failed_attempts, locked_until
|
||||
SELECT id, email, initials, display_name, color, role, permissions,
|
||||
created_at, last_login, failed_attempts, locked_until, custom_initials
|
||||
FROM users
|
||||
ORDER BY id
|
||||
`).all();
|
||||
@ -55,10 +55,10 @@ router.get('/users', (req, res) => {
|
||||
*/
|
||||
router.post('/users', async (req, res) => {
|
||||
try {
|
||||
const { username, password, displayName, email, role, permissions } = req.body;
|
||||
const { initials, password, displayName, email, role, permissions } = req.body;
|
||||
|
||||
// Validierung
|
||||
if (!username || !password || !displayName || !email) {
|
||||
if (!initials || !password || !displayName || !email) {
|
||||
return res.status(400).json({ error: 'Kürzel, Passwort, Anzeigename und E-Mail erforderlich' });
|
||||
}
|
||||
|
||||
@ -69,8 +69,8 @@ router.post('/users', async (req, res) => {
|
||||
}
|
||||
|
||||
// Kürzel muss genau 2 Buchstaben sein
|
||||
const usernameUpper = username.toUpperCase();
|
||||
if (!/^[A-Z]{2}$/.test(usernameUpper)) {
|
||||
const initialsUpper = initials.toUpperCase();
|
||||
if (!/^[A-Z]{2}$/.test(initialsUpper)) {
|
||||
return res.status(400).json({ error: 'Kürzel muss genau 2 Buchstaben sein (z.B. HG)' });
|
||||
}
|
||||
|
||||
@ -80,18 +80,18 @@ router.post('/users', async (req, res) => {
|
||||
|
||||
const db = getDb();
|
||||
|
||||
// Prüfen ob Kürzel bereits existiert
|
||||
const existing = db.prepare('SELECT id FROM users WHERE username = ?').get(usernameUpper);
|
||||
if (existing) {
|
||||
return res.status(400).json({ error: 'Kürzel bereits vergeben' });
|
||||
}
|
||||
|
||||
// Prüfen ob E-Mail bereits existiert
|
||||
const existingEmail = db.prepare('SELECT id FROM users WHERE email = ?').get(email.toLowerCase());
|
||||
if (existingEmail) {
|
||||
return res.status(400).json({ error: 'E-Mail bereits vergeben' });
|
||||
}
|
||||
|
||||
// Prüfen ob Kürzel bereits existiert
|
||||
const existingInitials = db.prepare('SELECT id FROM users WHERE initials = ?').get(initialsUpper);
|
||||
if (existingInitials) {
|
||||
return res.status(400).json({ error: 'Kürzel bereits vergeben' });
|
||||
}
|
||||
|
||||
// Passwort hashen
|
||||
const passwordHash = await bcrypt.hash(password, 12);
|
||||
|
||||
@ -100,23 +100,24 @@ router.post('/users', async (req, res) => {
|
||||
|
||||
// Benutzer erstellen
|
||||
const result = db.prepare(`
|
||||
INSERT INTO users (username, password_hash, display_name, color, role, permissions, email)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO users (email, initials, password_hash, display_name, color, role, permissions, custom_initials)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
usernameUpper,
|
||||
email.toLowerCase(),
|
||||
initialsUpper,
|
||||
passwordHash,
|
||||
displayName,
|
||||
defaultColor,
|
||||
role || 'user',
|
||||
JSON.stringify(permissions || []),
|
||||
email.toLowerCase()
|
||||
initialsUpper
|
||||
);
|
||||
|
||||
logger.info(`Admin ${req.user.username} hat Benutzer ${usernameUpper} erstellt`);
|
||||
logger.info(`Admin ${req.user.email} hat Benutzer ${initialsUpper} erstellt`);
|
||||
|
||||
res.status(201).json({
|
||||
id: result.lastInsertRowid,
|
||||
username: usernameUpper,
|
||||
initials: initialsUpper,
|
||||
displayName,
|
||||
email: email.toLowerCase(),
|
||||
color: defaultColor,
|
||||
@ -135,7 +136,7 @@ router.post('/users', async (req, res) => {
|
||||
router.put('/users/:id', async (req, res) => {
|
||||
try {
|
||||
const userId = parseInt(req.params.id);
|
||||
const { displayName, color, role, permissions, password, unlockAccount, email } = req.body;
|
||||
const { displayName, color, role, permissions, password, unlockAccount, email, initials } = req.body;
|
||||
|
||||
const db = getDb();
|
||||
|
||||
@ -206,6 +207,24 @@ router.put('/users/:id', async (req, res) => {
|
||||
params.push(email.toLowerCase());
|
||||
}
|
||||
|
||||
if (initials !== undefined) {
|
||||
// Validierung: Genau 2 Buchstaben
|
||||
const initialsUpper = initials.toUpperCase();
|
||||
if (!/^[A-Z]{2}$/.test(initialsUpper)) {
|
||||
return res.status(400).json({ error: 'Kürzel muss genau 2 Buchstaben sein (z.B. HG)' });
|
||||
}
|
||||
// Prüfen ob Kürzel bereits von anderem Benutzer verwendet wird
|
||||
const existingInitials = db.prepare('SELECT id FROM users WHERE initials = ? AND id != ?').get(initialsUpper, userId);
|
||||
if (existingInitials) {
|
||||
return res.status(400).json({ error: 'Kürzel bereits vergeben' });
|
||||
}
|
||||
updates.push('initials = ?');
|
||||
params.push(initialsUpper);
|
||||
// Auch custom_initials aktualisieren für Kompatibilität
|
||||
updates.push('custom_initials = ?');
|
||||
params.push(initialsUpper);
|
||||
}
|
||||
|
||||
if (updates.length === 0) {
|
||||
return res.status(400).json({ error: 'Keine Änderungen angegeben' });
|
||||
}
|
||||
@ -218,7 +237,7 @@ router.put('/users/:id', async (req, res) => {
|
||||
// Aktualisierten Benutzer zurueckgeben
|
||||
const updatedUser = db.prepare(`
|
||||
SELECT id, username, display_name, color, role, permissions, email,
|
||||
created_at, last_login, failed_attempts, locked_until
|
||||
created_at, last_login, failed_attempts, locked_until, custom_initials
|
||||
FROM users WHERE id = ?
|
||||
`).get(userId);
|
||||
|
||||
|
||||
@ -35,10 +35,15 @@ router.post('/login', async (req, res) => {
|
||||
|
||||
const db = getDb();
|
||||
|
||||
// Benutzer suchen: Zuerst nach Username "admin", dann nach E-Mail
|
||||
// Benutzer suchen: Admin kann mit "admin" einloggen, alle anderen mit E-Mail
|
||||
let user;
|
||||
// User per Username suchen (kann E-Mail-Adresse oder admin sein)
|
||||
user = db.prepare('SELECT * FROM users WHERE username = ?').get(username);
|
||||
if (username.toLowerCase() === 'admin') {
|
||||
// Admin-Login über spezielle E-Mail
|
||||
user = db.prepare('SELECT * FROM users WHERE email = ? AND role = ?').get('admin@taskmate.local', 'admin');
|
||||
} else {
|
||||
// Normale User loggen sich mit E-Mail ein
|
||||
user = db.prepare('SELECT * FROM users WHERE email = ?').get(username);
|
||||
}
|
||||
|
||||
// Audit-Log Eintrag vorbereiten
|
||||
const logAttempt = (userId, success) => {
|
||||
@ -131,7 +136,8 @@ router.post('/login', async (req, res) => {
|
||||
csrfToken,
|
||||
user: {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
initials: user.initials,
|
||||
displayName: user.display_name,
|
||||
color: user.color,
|
||||
role: user.role || 'user',
|
||||
@ -345,14 +351,15 @@ router.get('/users', authenticateToken, (req, res) => {
|
||||
const db = getDb();
|
||||
// Nur regulaere Benutzer (nicht Admins) fuer Aufgaben-Zuweisung
|
||||
const users = db.prepare(`
|
||||
SELECT id, username, display_name, color
|
||||
SELECT id, email, initials, display_name, color
|
||||
FROM users
|
||||
WHERE role != 'admin' OR role IS NULL
|
||||
`).all();
|
||||
|
||||
res.json(users.map(u => ({
|
||||
id: u.id,
|
||||
username: u.username,
|
||||
email: u.email,
|
||||
initials: u.initials,
|
||||
displayName: u.display_name,
|
||||
color: u.color
|
||||
})));
|
||||
|
||||
BIN
data/taskmate.db
BIN
data/taskmate.db
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
@ -108,6 +108,14 @@
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.admin-user-initials {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.admin-user-info {
|
||||
|
||||
@ -150,15 +150,34 @@ class AdminManager {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt die Initialen des Benutzers
|
||||
*/
|
||||
getInitials(user) {
|
||||
// Verwende primär das initials Feld
|
||||
if (user.initials) {
|
||||
return user.initials.toUpperCase();
|
||||
}
|
||||
|
||||
// Fallback auf custom_initials für Kompatibilität
|
||||
if (user.custom_initials) {
|
||||
return user.custom_initials.toUpperCase();
|
||||
}
|
||||
|
||||
// Letzer Fallback
|
||||
return 'XX';
|
||||
}
|
||||
|
||||
renderUserCard(user) {
|
||||
const initial = (user.display_name || user.username).charAt(0).toUpperCase();
|
||||
const initials = this.getInitials(user);
|
||||
console.log('Rendering user card for:', user.email || user.username, '- Initials:', initials);
|
||||
const isLocked = user.locked_until && new Date(user.locked_until) > new Date();
|
||||
const permissions = user.permissions || [];
|
||||
|
||||
return `
|
||||
<div class="admin-user-card" data-user-id="${user.id}">
|
||||
<div class="admin-user-avatar" style="background-color: ${user.color || '#808080'}">
|
||||
${initial}
|
||||
<span class="admin-user-initials">${initials}</span>
|
||||
</div>
|
||||
<div class="admin-user-info">
|
||||
<div class="admin-user-name">${this.escapeHtml(user.display_name)}</div>
|
||||
@ -236,8 +255,8 @@ class AdminManager {
|
||||
this.currentEditUser = user;
|
||||
this.userModalTitle.textContent = 'Benutzer bearbeiten';
|
||||
this.editUserId.value = user.id;
|
||||
this.usernameInput.value = user.username;
|
||||
this.usernameInput.disabled = true; // Username cannot be changed
|
||||
this.usernameInput.value = user.initials || '';
|
||||
this.usernameInput.disabled = false; // Initials can be changed
|
||||
this.displayNameInput.value = user.display_name;
|
||||
this.emailInput.value = user.email || '';
|
||||
this.emailInput.disabled = false;
|
||||
@ -285,9 +304,8 @@ class AdminManager {
|
||||
permissions: this.roleSelect.value === 'admin' ? [] : this.getSelectedPermissions()
|
||||
};
|
||||
|
||||
if (!isEdit) {
|
||||
data.username = this.usernameInput.value.trim().toUpperCase();
|
||||
}
|
||||
// Kürzel immer mitschicken (bei Create und Update)
|
||||
data.initials = this.usernameInput.value.trim().toUpperCase();
|
||||
|
||||
if (this.passwordInput.value) {
|
||||
data.password = this.passwordInput.value;
|
||||
|
||||
@ -135,12 +135,14 @@ class AuthManager {
|
||||
// Get user initials
|
||||
getUserInitials() {
|
||||
if (!this.user) return '?';
|
||||
|
||||
return this.user.username
|
||||
.split(' ')
|
||||
.map(part => part.charAt(0).toUpperCase())
|
||||
.slice(0, 2)
|
||||
.join('');
|
||||
|
||||
// Verwende das initials Feld direkt
|
||||
if (this.user.initials) {
|
||||
return this.user.initials.toUpperCase();
|
||||
}
|
||||
|
||||
// Fallback für alte Daten
|
||||
return '??';
|
||||
}
|
||||
|
||||
// Get user color
|
||||
|
||||
@ -372,6 +372,7 @@ class BoardManager {
|
||||
const currentUser = users.find(u => u.id === assignee.id);
|
||||
const color = currentUser?.color || assignee.color || '#888';
|
||||
const name = currentUser?.display_name || assignee.display_name || assignee.username || 'Benutzer';
|
||||
const initials = currentUser?.initials || assignee.initials || getInitials(name);
|
||||
|
||||
const avatar = createElement('span', {
|
||||
className: 'avatar task-assignee-avatar stacked',
|
||||
@ -380,7 +381,7 @@ class BoardManager {
|
||||
zIndex: 10 - index
|
||||
},
|
||||
title: name
|
||||
}, [getInitials(name)]);
|
||||
}, [initials]);
|
||||
assigneesContainer.appendChild(avatar);
|
||||
});
|
||||
|
||||
|
||||
@ -868,7 +868,7 @@ class CalendarViewManager {
|
||||
if (task.assignees && task.assignees.length > 0) {
|
||||
task.assignees.forEach(assignee => {
|
||||
const currentUser = users.find(u => u.id === assignee.id);
|
||||
const initials = currentUser?.username || assignee.username || '??';
|
||||
const initials = currentUser?.initials || assignee.initials || '??';
|
||||
const color = currentUser?.color || assignee.color || '#6B7280';
|
||||
badges.push({ initials, color });
|
||||
});
|
||||
@ -879,7 +879,7 @@ class CalendarViewManager {
|
||||
if (task.assignedTo) {
|
||||
const user = users.find(u => u.id === task.assignedTo);
|
||||
if (user) {
|
||||
const initials = user.username || '??';
|
||||
const initials = user.initials || '??';
|
||||
badges.push({ initials, color: user.color || '#6B7280' });
|
||||
return badges;
|
||||
}
|
||||
|
||||
@ -251,7 +251,7 @@ class DashboardManager {
|
||||
createElement('span', {
|
||||
className: 'avatar avatar-sm',
|
||||
style: { backgroundColor: task.assignee.color || '#888' }
|
||||
}, [getInitials(task.assignee.username)])
|
||||
}, [task.assignee.initials || getInitials(task.assignee.username || '??')])
|
||||
]) : null
|
||||
].filter(Boolean));
|
||||
|
||||
|
||||
@ -443,7 +443,7 @@ class ListViewManager {
|
||||
className: 'avatar',
|
||||
style: { backgroundColor: user.color || '#6366F1' },
|
||||
title: user.displayName // Tooltip zeigt Name beim Hover
|
||||
}, [getInitials(user.displayName)]);
|
||||
}, [user.initials || getInitials(user.displayName)]);
|
||||
avatarContainer.appendChild(avatar);
|
||||
}
|
||||
});
|
||||
|
||||
@ -818,7 +818,7 @@ class TaskModalManager {
|
||||
const avatar = createElement('div', {
|
||||
class: 'multi-select-option-avatar',
|
||||
style: `background-color: ${user.color || '#6366F1'}`
|
||||
}, [getInitials(user.display_name || user.username)]);
|
||||
}, [user.initials || getInitials(user.display_name || user.username)]);
|
||||
|
||||
const name = createElement('span', { class: 'multi-select-option-name' }, [user.display_name || user.username]);
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
* Offline support and caching
|
||||
*/
|
||||
|
||||
const CACHE_VERSION = '189';
|
||||
const CACHE_VERSION = '197';
|
||||
const CACHE_NAME = 'taskmate-v' + CACHE_VERSION;
|
||||
const STATIC_CACHE_NAME = 'taskmate-static-v' + CACHE_VERSION;
|
||||
const DYNAMIC_CACHE_NAME = 'taskmate-dynamic-v' + CACHE_VERSION;
|
||||
|
||||
5977
logs/app.log
5977
logs/app.log
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
In neuem Issue referenzieren
Einen Benutzer sperren