Dieser Commit ist enthalten in:
Claude Project Manager
2025-07-08 13:13:46 +02:00
Ursprung d1667f9e0d
Commit 5f32daf3a4
15 geänderte Dateien mit 1139 neuen und 136 gelöschten Zeilen

Datei anzeigen

@ -8,6 +8,8 @@ from gui.styles import COLORS, FONTS
from utils.logger import logger
import json
from pathlib import Path
from tkinter import filedialog
import os
class SettingsDialog(ctk.CTkToplevel):
@ -19,7 +21,7 @@ class SettingsDialog(ctk.CTkToplevel):
# Window setup
self.title("Einstellungen")
self.minsize(500, 450)
self.minsize(650, 550)
self.resizable(True, True)
# Make modal
@ -56,96 +58,31 @@ class SettingsDialog(ctk.CTkToplevel):
)
title_label.pack(pady=(0, 20))
# Activity Server Section
activity_section = ctk.CTkFrame(main_frame, fg_color=COLORS['bg_secondary'])
activity_section.pack(fill="x", pady=(0, 15))
activity_header = ctk.CTkLabel(
activity_section,
text="👥 Team-Aktivität Server",
font=FONTS['body'],
text_color=COLORS['text_primary']
)
activity_header.pack(anchor="w", padx=15, pady=(10, 5))
# Server URL
url_label = ctk.CTkLabel(
activity_section,
text="Server URL:",
font=FONTS['small'],
text_color=COLORS['text_secondary']
)
url_label.pack(anchor="w", padx=30, pady=(5, 0))
self.server_url_var = ctk.StringVar(value=self.settings.get("activity_server_url", "http://91.99.192.14:3001"))
self.server_url_entry = ctk.CTkEntry(
activity_section,
textvariable=self.server_url_var,
width=300,
fg_color=COLORS['bg_primary']
)
self.server_url_entry.pack(padx=30, pady=(0, 5))
# API Key
api_label = ctk.CTkLabel(
activity_section,
text="API Key:",
font=FONTS['small'],
text_color=COLORS['text_secondary']
)
api_label.pack(anchor="w", padx=30, pady=(5, 0))
self.api_key_var = ctk.StringVar(value=self.settings.get("activity_api_key", ""))
self.api_key_entry = ctk.CTkEntry(
activity_section,
textvariable=self.api_key_var,
width=300,
fg_color=COLORS['bg_primary'],
show="*"
)
self.api_key_entry.pack(padx=30, pady=(0, 5))
# User Name
user_label = ctk.CTkLabel(
activity_section,
text="Benutzername:",
font=FONTS['small'],
text_color=COLORS['text_secondary']
)
user_label.pack(anchor="w", padx=30, pady=(5, 0))
self.user_name_var = ctk.StringVar(value=self.settings.get("activity_user_name", ""))
self.user_name_entry = ctk.CTkEntry(
activity_section,
textvariable=self.user_name_var,
width=300,
fg_color=COLORS['bg_primary']
)
self.user_name_entry.pack(padx=30, pady=(0, 5))
# Test connection button
test_btn = ctk.CTkButton(
activity_section,
text="Verbindung testen",
command=self.test_activity_connection,
fg_color=COLORS['bg_tile'],
hover_color=COLORS['bg_tile_hover'],
# Tab view
self.tabview = ctk.CTkTabview(
main_frame,
fg_color=COLORS['bg_secondary'],
segmented_button_fg_color=COLORS['bg_tile'],
segmented_button_selected_color=COLORS['accent_primary'],
segmented_button_selected_hover_color=COLORS['accent_hover'],
segmented_button_unselected_color=COLORS['bg_tile'],
segmented_button_unselected_hover_color=COLORS['bg_tile_hover'],
text_color=COLORS['text_primary'],
width=150
text_color_disabled=COLORS['text_dim']
)
test_btn.pack(pady=(5, 10))
self.tabview.pack(fill="both", expand=True)
# Status label
self.connection_status_label = ctk.CTkLabel(
activity_section,
text="",
font=FONTS['small'],
text_color=COLORS['text_secondary'],
height=20
)
self.connection_status_label.pack(pady=(0, 10))
# Add tabs
self.tabview.add("Allgemein")
self.tabview.add("Gitea")
self.tabview.add("Team-Aktivität")
# Buttons
# Setup each tab
self.setup_general_tab()
self.setup_gitea_tab()
self.setup_activity_tab()
# Buttons (at bottom of main frame)
button_frame = ctk.CTkFrame(main_frame, fg_color="transparent")
button_frame.pack(fill="x", pady=(20, 0))
@ -184,6 +121,264 @@ class SettingsDialog(ctk.CTkToplevel):
)
save_btn.pack(side="right", padx=(5, 0))
def setup_general_tab(self):
"""Setup general settings tab"""
tab = self.tabview.tab("Allgemein")
# Container with padding
container = ctk.CTkFrame(tab, fg_color="transparent")
container.pack(fill="both", expand=True, padx=20, pady=20)
# Placeholder for future general settings
info_label = ctk.CTkLabel(
container,
text="Allgemeine Einstellungen werden hier angezeigt.\n(Aktuell keine verfügbar)",
font=FONTS['body'],
text_color=COLORS['text_dim']
)
info_label.pack(pady=50)
def setup_gitea_tab(self):
"""Setup Gitea settings tab"""
tab = self.tabview.tab("Gitea")
# Scrollable container
container = ctk.CTkScrollableFrame(tab, fg_color="transparent")
container.pack(fill="both", expand=True, padx=20, pady=20)
# Clone Directory Section
clone_section = ctk.CTkFrame(container, fg_color=COLORS['bg_tile'])
clone_section.pack(fill="x", pady=(0, 15))
clone_header = ctk.CTkLabel(
clone_section,
text="📁 Clone-Verzeichnis",
font=FONTS['body'],
text_color=COLORS['text_primary']
)
clone_header.pack(anchor="w", padx=15, pady=(10, 5))
# Clone directory path
clone_path_frame = ctk.CTkFrame(clone_section, fg_color="transparent")
clone_path_frame.pack(fill="x", padx=30, pady=(5, 10))
default_clone_dir = str(Path.home() / "GiteaRepos")
self.clone_dir_var = ctk.StringVar(value=self.settings.get("gitea_clone_directory", default_clone_dir))
self.clone_dir_entry = ctk.CTkEntry(
clone_path_frame,
textvariable=self.clone_dir_var,
width=350,
fg_color=COLORS['bg_primary']
)
self.clone_dir_entry.pack(side="left", padx=(0, 10))
browse_btn = ctk.CTkButton(
clone_path_frame,
text="Durchsuchen...",
command=self.browse_clone_directory,
fg_color=COLORS['bg_secondary'],
hover_color=COLORS['bg_tile_hover'],
text_color=COLORS['text_primary'],
width=100
)
browse_btn.pack(side="left")
# Info label
info_label = ctk.CTkLabel(
clone_section,
text="Standardverzeichnis für geklonte Repositories",
font=FONTS['small'],
text_color=COLORS['text_dim']
)
info_label.pack(anchor="w", padx=30, pady=(0, 10))
# Server Configuration Section
server_section = ctk.CTkFrame(container, fg_color=COLORS['bg_tile'])
server_section.pack(fill="x", pady=(0, 15))
server_header = ctk.CTkLabel(
server_section,
text="🌐 Gitea Server",
font=FONTS['body'],
text_color=COLORS['text_primary']
)
server_header.pack(anchor="w", padx=15, pady=(10, 5))
# Server URL
url_label = ctk.CTkLabel(
server_section,
text="Server URL:",
font=FONTS['small'],
text_color=COLORS['text_secondary']
)
url_label.pack(anchor="w", padx=30, pady=(5, 0))
self.gitea_url_var = ctk.StringVar(value=self.settings.get("gitea_server_url", "https://gitea-undso.intelsight.de"))
self.gitea_url_entry = ctk.CTkEntry(
server_section,
textvariable=self.gitea_url_var,
width=350,
fg_color=COLORS['bg_primary']
)
self.gitea_url_entry.pack(padx=30, pady=(0, 5))
# API Token
token_label = ctk.CTkLabel(
server_section,
text="API Token:",
font=FONTS['small'],
text_color=COLORS['text_secondary']
)
token_label.pack(anchor="w", padx=30, pady=(5, 0))
self.gitea_token_var = ctk.StringVar(value=self.settings.get("gitea_api_token", ""))
self.gitea_token_entry = ctk.CTkEntry(
server_section,
textvariable=self.gitea_token_var,
width=350,
fg_color=COLORS['bg_primary'],
show="*"
)
self.gitea_token_entry.pack(padx=30, pady=(0, 5))
# Username
user_label = ctk.CTkLabel(
server_section,
text="Benutzername:",
font=FONTS['small'],
text_color=COLORS['text_secondary']
)
user_label.pack(anchor="w", padx=30, pady=(5, 0))
self.gitea_user_var = ctk.StringVar(value=self.settings.get("gitea_username", ""))
self.gitea_user_entry = ctk.CTkEntry(
server_section,
textvariable=self.gitea_user_var,
width=350,
fg_color=COLORS['bg_primary']
)
self.gitea_user_entry.pack(padx=30, pady=(0, 10))
# Test connection button
test_btn = ctk.CTkButton(
server_section,
text="Verbindung testen",
command=self.test_gitea_connection,
fg_color=COLORS['bg_secondary'],
hover_color=COLORS['bg_tile_hover'],
text_color=COLORS['text_primary'],
width=150
)
test_btn.pack(pady=(5, 10))
# Status label
self.gitea_status_label = ctk.CTkLabel(
server_section,
text="",
font=FONTS['small'],
text_color=COLORS['text_secondary'],
height=20
)
self.gitea_status_label.pack(pady=(0, 10))
def setup_activity_tab(self):
"""Setup activity server settings tab"""
tab = self.tabview.tab("Team-Aktivität")
# Container with padding
container = ctk.CTkFrame(tab, fg_color="transparent")
container.pack(fill="both", expand=True, padx=20, pady=20)
# Activity Server Section
activity_section = ctk.CTkFrame(container, fg_color=COLORS['bg_tile'])
activity_section.pack(fill="x", pady=(0, 15))
activity_header = ctk.CTkLabel(
activity_section,
text="👥 Team-Aktivität Server",
font=FONTS['body'],
text_color=COLORS['text_primary']
)
activity_header.pack(anchor="w", padx=15, pady=(10, 5))
# Server URL
url_label = ctk.CTkLabel(
activity_section,
text="Server URL:",
font=FONTS['small'],
text_color=COLORS['text_secondary']
)
url_label.pack(anchor="w", padx=30, pady=(5, 0))
self.server_url_var = ctk.StringVar(value=self.settings.get("activity_server_url", "http://91.99.192.14:3001"))
self.server_url_entry = ctk.CTkEntry(
activity_section,
textvariable=self.server_url_var,
width=350,
fg_color=COLORS['bg_primary']
)
self.server_url_entry.pack(padx=30, pady=(0, 5))
# API Key
api_label = ctk.CTkLabel(
activity_section,
text="API Key:",
font=FONTS['small'],
text_color=COLORS['text_secondary']
)
api_label.pack(anchor="w", padx=30, pady=(5, 0))
self.api_key_var = ctk.StringVar(value=self.settings.get("activity_api_key", ""))
self.api_key_entry = ctk.CTkEntry(
activity_section,
textvariable=self.api_key_var,
width=350,
fg_color=COLORS['bg_primary'],
show="*"
)
self.api_key_entry.pack(padx=30, pady=(0, 5))
# User Name
user_label = ctk.CTkLabel(
activity_section,
text="Benutzername:",
font=FONTS['small'],
text_color=COLORS['text_secondary']
)
user_label.pack(anchor="w", padx=30, pady=(5, 0))
self.user_name_var = ctk.StringVar(value=self.settings.get("activity_user_name", ""))
self.user_name_entry = ctk.CTkEntry(
activity_section,
textvariable=self.user_name_var,
width=350,
fg_color=COLORS['bg_primary']
)
self.user_name_entry.pack(padx=30, pady=(0, 5))
# Test connection button
test_btn = ctk.CTkButton(
activity_section,
text="Verbindung testen",
command=self.test_activity_connection,
fg_color=COLORS['bg_secondary'],
hover_color=COLORS['bg_tile_hover'],
text_color=COLORS['text_primary'],
width=150
)
test_btn.pack(pady=(5, 10))
# Status label
self.connection_status_label = ctk.CTkLabel(
activity_section,
text="",
font=FONTS['small'],
text_color=COLORS['text_secondary'],
height=20
)
self.connection_status_label.pack(pady=(0, 10))
def load_settings(self):
"""Load settings from file"""
try:
@ -205,49 +400,141 @@ class SettingsDialog(ctk.CTkToplevel):
except Exception as e:
logger.error(f"Failed to save UI settings: {e}")
def browse_clone_directory(self):
"""Browse for clone directory"""
current_dir = self.clone_dir_var.get()
if not os.path.exists(current_dir):
current_dir = str(Path.home())
directory = filedialog.askdirectory(
title="Clone-Verzeichnis auswählen",
initialdir=current_dir,
parent=self
)
if directory:
self.clone_dir_var.set(directory)
logger.info(f"Selected clone directory: {directory}")
def test_gitea_connection(self):
"""Test connection to Gitea server"""
import requests
server_url = self.gitea_url_var.get().strip()
api_token = self.gitea_token_var.get().strip()
if not server_url:
self.gitea_status_label.configure(
text="⚠️ Bitte Server URL eingeben",
text_color=COLORS['accent_warning']
)
return
self.gitea_status_label.configure(
text="🔄 Teste Verbindung...",
text_color=COLORS['text_secondary']
)
self.update()
try:
# Try to connect to the Gitea API
headers = {}
if api_token:
headers["Authorization"] = f"token {api_token}"
response = requests.get(
f"{server_url}/api/v1/version",
timeout=5,
headers=headers
)
if response.status_code == 200:
version = response.json().get('version', 'Unknown')
self.gitea_status_label.configure(
text=f"✅ Verbindung erfolgreich! (Gitea {version})",
text_color=COLORS['accent_success']
)
logger.info(f"Gitea connection successful: {server_url}, version: {version}")
else:
self.gitea_status_label.configure(
text=f"❌ Server antwortet mit Status {response.status_code}",
text_color=COLORS['accent_error']
)
logger.warning(f"Gitea server returned status {response.status_code}")
except requests.exceptions.ConnectionError:
self.gitea_status_label.configure(
text="❌ Server nicht erreichbar",
text_color=COLORS['accent_error']
)
logger.error(f"Gitea server not reachable: {server_url}")
except requests.exceptions.Timeout:
self.gitea_status_label.configure(
text="❌ Verbindung Timeout",
text_color=COLORS['accent_error']
)
logger.error(f"Gitea server connection timeout: {server_url}")
except Exception as e:
self.gitea_status_label.configure(
text=f"❌ Fehler: {str(e)}",
text_color=COLORS['accent_error']
)
logger.error(f"Gitea server connection error: {e}")
def save_settings_only(self):
"""Save settings without applying to service or closing dialog"""
# Get values
# Get activity values
server_url = self.server_url_var.get().strip()
api_key = self.api_key_var.get().strip()
user_name = self.user_name_var.get().strip()
# Get Gitea values
gitea_url = self.gitea_url_var.get().strip()
gitea_token = self.gitea_token_var.get().strip()
gitea_user = self.gitea_user_var.get().strip()
clone_dir = self.clone_dir_var.get().strip()
# Update settings
self.settings["activity_server_url"] = server_url
self.settings["activity_api_key"] = api_key
self.settings["activity_user_name"] = user_name
self.settings["gitea_server_url"] = gitea_url
self.settings["gitea_api_token"] = gitea_token
self.settings["gitea_username"] = gitea_user
self.settings["gitea_clone_directory"] = clone_dir
self.save_settings()
# Show confirmation
self.connection_status_label.configure(
text="✅ Einstellungen gespeichert!",
text_color=COLORS['accent_success']
)
logger.info("Settings saved successfully")
# Show confirmation on the active tab
current_tab = self.tabview.get()
if current_tab == "Team-Aktivität":
self.connection_status_label.configure(
text="✅ Einstellungen gespeichert!",
text_color=COLORS['accent_success']
)
self.after(2000, lambda: self.connection_status_label.configure(text=""))
elif current_tab == "Gitea":
self.gitea_status_label.configure(
text="✅ Einstellungen gespeichert!",
text_color=COLORS['accent_success']
)
self.after(2000, lambda: self.gitea_status_label.configure(text=""))
# Clear status after 2 seconds
self.after(2000, lambda: self.connection_status_label.configure(text=""))
logger.info("Settings saved successfully")
def apply_settings(self):
"""Apply the selected settings"""
import uuid
from services.activity_sync import activity_service
# Get values
server_url = self.server_url_var.get().strip()
api_key = self.api_key_var.get().strip()
user_name = self.user_name_var.get().strip()
# Save all settings first
self.save_settings_only()
# Update settings
self.settings["activity_server_url"] = server_url
self.settings["activity_api_key"] = api_key
self.settings["activity_user_name"] = user_name
self.save_settings()
# Update activity service
activity_service.server_url = server_url
activity_service.api_key = api_key
activity_service.user_name = user_name
# Apply activity service settings
activity_service.server_url = self.settings.get("activity_server_url", "")
activity_service.api_key = self.settings.get("activity_api_key", "")
activity_service.user_name = self.settings.get("activity_user_name", "")
activity_service.user_id = self.settings.get("activity_user_id", str(uuid.uuid4()))
# Save user ID if newly generated
@ -267,9 +554,65 @@ class SettingsDialog(ctk.CTkToplevel):
logger.info("Activity server settings applied")
# Apply Gitea settings to config
self.update_gitea_config()
# Close dialog
self.destroy()
def update_gitea_config(self):
"""Update Gitea configuration in the application"""
try:
# Import here to avoid circular imports
from src.gitea.gitea_client import gitea_config
from src.gitea.git_operations import git_ops
# Update Gitea config
if hasattr(gitea_config, 'base_url'):
gitea_config.base_url = self.settings.get("gitea_server_url", gitea_config.base_url)
if hasattr(gitea_config, 'api_token'):
gitea_config.api_token = self.settings.get("gitea_api_token", gitea_config.api_token)
if hasattr(gitea_config, 'username'):
gitea_config.username = self.settings.get("gitea_username", gitea_config.username)
# Update clone directory
clone_dir = self.settings.get("gitea_clone_directory")
if clone_dir:
# Re-initialize git_ops to use new settings
from src.gitea.git_operations import init_git_ops
init_git_ops()
# Now update the clone directory
if hasattr(git_ops, 'default_clone_dir'):
git_ops.default_clone_dir = Path(clone_dir)
logger.info(f"Updated git_ops clone directory to: {clone_dir}")
# Update all existing RepositoryManager instances
# This ensures that any git_ops instances in the application get the new directory
try:
# Update in main window if it exists
parent = self.master
if hasattr(parent, 'gitea_handler') and hasattr(parent.gitea_handler, 'repo_manager'):
if hasattr(parent.gitea_handler.repo_manager, 'git_ops'):
parent.gitea_handler.repo_manager.git_ops.default_clone_dir = Path(clone_dir)
logger.info("Updated main window's git_ops clone directory")
# Update in sidebar gitea explorer if it exists
if hasattr(parent, 'sidebar') and hasattr(parent.sidebar, 'gitea_explorer'):
if hasattr(parent.sidebar.gitea_explorer, 'repo_manager'):
if hasattr(parent.sidebar.gitea_explorer.repo_manager, 'git_ops'):
parent.sidebar.gitea_explorer.repo_manager.git_ops.default_clone_dir = Path(clone_dir)
logger.info("Updated sidebar's git_ops clone directory")
except Exception as e:
logger.warning(f"Could not update all git_ops instances: {e}")
logger.info("Gitea configuration updated")
except ImportError as e:
logger.warning(f"Could not update Gitea config: {e}")
except Exception as e:
logger.error(f"Error updating Gitea config: {e}")
def test_activity_connection(self):
"""Test connection to activity server"""
import requests