UI-Redesign: AegisSight Design, Filter-Popover, Header-Umbau
- Session-Timeout auf 60 Minuten erhöht (ACCESS_TOKEN_EXPIRY + SESSION_TIMEOUT) - AegisSight Light Theme: Gold-Akzent (#C8A851) statt Indigo - Navigation-Tabs in eigene Zeile unter Header verschoben (HTML-Struktur) - Filter-Bar durch kompaktes Popover mit Checkboxen ersetzt (Mehrfachauswahl) - Archiv-Funktion repariert (lädt jetzt per API statt leerem Store) - Filter-Bugs behoben: Reset-Button ID, Default-Werte, Ohne-Datum-Filter - Mehrspalten-Layout Feature entfernt - Online-Status vom Header an User-Avatar verschoben (grüner Punkt) - Lupen-Icon entfernt - CLAUDE.md: Docker-Deploy und CSS-Tricks Regeln aktualisiert Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import { $, $$ } from './utils.js';
|
||||
import { enhanceMobileSwipe } from './mobile-swipe.js';
|
||||
|
||||
class MobileManager {
|
||||
constructor() {
|
||||
@@ -21,6 +22,8 @@ class MobileManager {
|
||||
this.touchStartTime = 0;
|
||||
this.isSwiping = false;
|
||||
this.swipeDirection = null;
|
||||
this.currentColumnIndex = 0;
|
||||
this.swipeTarget = null; // 'board' or 'header'
|
||||
|
||||
// Touch drag & drop state
|
||||
this.touchDraggedElement = null;
|
||||
@@ -87,6 +90,9 @@ class MobileManager {
|
||||
this.updateUserInfo();
|
||||
});
|
||||
|
||||
// Enhance with new swipe functionality
|
||||
enhanceMobileSwipe(this);
|
||||
|
||||
console.log('[Mobile] Initialized');
|
||||
}
|
||||
|
||||
@@ -220,8 +226,18 @@ class MobileManager {
|
||||
$$('.view').forEach(v => {
|
||||
const viewName = v.id.replace('view-', '');
|
||||
const isActive = viewName === view;
|
||||
v.classList.toggle('active', isActive);
|
||||
v.classList.toggle('hidden', !isActive);
|
||||
|
||||
if (isActive) {
|
||||
v.classList.add('active');
|
||||
v.classList.remove('hidden');
|
||||
// Force display for mobile
|
||||
if (this.isMobile) {
|
||||
v.style.display = '';
|
||||
}
|
||||
} else {
|
||||
v.classList.remove('active');
|
||||
v.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
// Update mobile nav
|
||||
@@ -300,41 +316,42 @@ class MobileManager {
|
||||
bindSwipeEvents() {
|
||||
if (!this.mainContent) return;
|
||||
|
||||
this.mainContent.addEventListener('touchstart', (e) => this.handleSwipeStart(e), { passive: true });
|
||||
this.mainContent.addEventListener('touchmove', (e) => this.handleSwipeMove(e), { passive: false });
|
||||
this.mainContent.addEventListener('touchend', (e) => this.handleSwipeEnd(e), { passive: true });
|
||||
this.mainContent.addEventListener('touchcancel', () => this.resetSwipe(), { passive: true });
|
||||
// Swipe für Board-Container (Spalten-Navigation)
|
||||
const boardContainer = document.querySelector('.board-container');
|
||||
if (boardContainer) {
|
||||
boardContainer.addEventListener('touchstart', (e) => this.handleBoardSwipeStart(e), { passive: true });
|
||||
boardContainer.addEventListener('touchmove', (e) => this.handleBoardSwipeMove(e), { passive: false });
|
||||
boardContainer.addEventListener('touchend', (e) => this.handleBoardSwipeEnd(e), { passive: true });
|
||||
boardContainer.addEventListener('touchcancel', () => this.resetSwipe(), { passive: true });
|
||||
}
|
||||
|
||||
// Swipe für Header (View-Navigation)
|
||||
const header = document.querySelector('.header');
|
||||
if (header) {
|
||||
header.addEventListener('touchstart', (e) => this.handleHeaderSwipeStart(e), { passive: true });
|
||||
header.addEventListener('touchmove', (e) => this.handleHeaderSwipeMove(e), { passive: false });
|
||||
header.addEventListener('touchend', (e) => this.handleHeaderSwipeEnd(e), { passive: true });
|
||||
header.addEventListener('touchcancel', () => this.resetSwipe(), { passive: true });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle swipe start
|
||||
* Handle board swipe start (für Spalten-Navigation)
|
||||
*/
|
||||
handleSwipeStart(e) {
|
||||
if (!this.isMobile) return;
|
||||
handleBoardSwipeStart(e) {
|
||||
if (!this.isMobile || this.currentView !== 'board') return;
|
||||
if (this.isMenuOpen || $('.modal-overlay:not(.hidden)')) return;
|
||||
|
||||
// Don't swipe if menu is open
|
||||
if (this.isMenuOpen) return;
|
||||
|
||||
// Don't swipe if modal is open
|
||||
if ($('.modal-overlay:not(.hidden)')) return;
|
||||
|
||||
// Don't swipe on specific interactive elements, but allow swipe in column-body
|
||||
// Don't swipe on interactive elements
|
||||
const target = e.target;
|
||||
if (target.closest('.modal') ||
|
||||
target.closest('.calendar-grid') ||
|
||||
target.closest('.knowledge-entry-list') ||
|
||||
target.closest('.list-table') ||
|
||||
target.closest('input') ||
|
||||
target.closest('textarea') ||
|
||||
if (target.closest('button') ||
|
||||
target.closest('input') ||
|
||||
target.closest('textarea') ||
|
||||
target.closest('select') ||
|
||||
target.closest('button') ||
|
||||
target.closest('a[href]') ||
|
||||
target.closest('.task-card .priority-stars') ||
|
||||
target.closest('.task-card .task-counts')) {
|
||||
target.closest('.task-card')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only single touch
|
||||
if (e.touches.length !== 1) return;
|
||||
|
||||
this.touchStartX = e.touches[0].clientX;
|
||||
@@ -342,12 +359,79 @@ class MobileManager {
|
||||
this.touchStartTime = Date.now();
|
||||
this.isSwiping = false;
|
||||
this.swipeDirection = null;
|
||||
this.swipeTarget = 'board';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle swipe move
|
||||
* Handle header swipe start (für View-Navigation)
|
||||
*/
|
||||
handleSwipeMove(e) {
|
||||
handleHeaderSwipeStart(e) {
|
||||
if (!this.isMobile) return;
|
||||
if (this.isMenuOpen || $('.modal-overlay:not(.hidden)')) return;
|
||||
|
||||
// Nur in der Header-Region swipen
|
||||
if (!e.target.closest('.header')) return;
|
||||
|
||||
// Nicht auf Buttons swipen
|
||||
if (e.target.closest('button') || e.target.closest('.view-tab')) return;
|
||||
|
||||
if (e.touches.length !== 1) return;
|
||||
|
||||
this.touchStartX = e.touches[0].clientX;
|
||||
this.touchStartY = e.touches[0].clientY;
|
||||
this.touchStartTime = Date.now();
|
||||
this.isSwiping = false;
|
||||
this.swipeDirection = null;
|
||||
this.swipeTarget = 'header';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle board swipe move
|
||||
*/
|
||||
handleBoardSwipeMove(e) {
|
||||
if (!this.isMobile || this.touchStartX === 0 || this.swipeTarget !== 'board') return;
|
||||
|
||||
const touch = e.touches[0];
|
||||
this.touchCurrentX = touch.clientX;
|
||||
this.touchCurrentY = touch.clientY;
|
||||
|
||||
const deltaX = this.touchCurrentX - this.touchStartX;
|
||||
const deltaY = this.touchCurrentY - this.touchStartY;
|
||||
|
||||
// Determine direction
|
||||
if (!this.swipeDirection && (Math.abs(deltaX) > 10 || Math.abs(deltaY) > 10)) {
|
||||
if (Math.abs(deltaX) > Math.abs(deltaY) * 1.5) {
|
||||
this.swipeDirection = 'horizontal';
|
||||
this.isSwiping = true;
|
||||
document.body.classList.add('is-swiping');
|
||||
} else {
|
||||
this.swipeDirection = 'vertical';
|
||||
this.resetSwipe();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.swipeDirection !== 'horizontal') return;
|
||||
e.preventDefault();
|
||||
|
||||
// Visual feedback für Spalten-Navigation
|
||||
const columns = $$('.column');
|
||||
if (deltaX > this.SWIPE_THRESHOLD && this.currentColumnIndex > 0) {
|
||||
this.swipeIndicatorLeft?.classList.add('visible');
|
||||
this.swipeIndicatorRight?.classList.remove('visible');
|
||||
} else if (deltaX < -this.SWIPE_THRESHOLD && this.currentColumnIndex < columns.length - 1) {
|
||||
this.swipeIndicatorRight?.classList.add('visible');
|
||||
this.swipeIndicatorLeft?.classList.remove('visible');
|
||||
} else {
|
||||
this.swipeIndicatorLeft?.classList.remove('visible');
|
||||
this.swipeIndicatorRight?.classList.remove('visible');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle header swipe move
|
||||
*/
|
||||
handleHeaderSwipeMove(e) {
|
||||
if (!this.isMobile || this.touchStartX === 0) return;
|
||||
|
||||
const touch = e.touches[0];
|
||||
@@ -412,10 +496,11 @@ class MobileManager {
|
||||
* Handle swipe end
|
||||
*/
|
||||
handleSwipeEnd() {
|
||||
if (!this.isSwiping || this.swipeDirection !== 'horizontal') {
|
||||
this.resetSwipe();
|
||||
return;
|
||||
}
|
||||
if (!this.isMobile || this.touchStartX === 0 || this.swipeTarget !== 'header') return;
|
||||
|
||||
const touch = e.touches[0];
|
||||
this.touchCurrentX = touch.clientX;
|
||||
this.touchCurrentY = touch.clientY;
|
||||
|
||||
const deltaX = this.touchCurrentX - this.touchStartX;
|
||||
const deltaTime = Date.now() - this.touchStartTime;
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren