778 Zeilen
22 KiB
JavaScript
778 Zeilen
22 KiB
JavaScript
/**
|
|
* TASKMATE - Coding Manager
|
|
* =========================
|
|
* Verwaltung von Server-Anwendungen mit Claude/Codex Integration
|
|
*/
|
|
|
|
import api from './api.js';
|
|
import { escapeHtml } from './utils.js';
|
|
|
|
// Toast-Funktion (verwendet das globale Toast-Event)
|
|
function showToast(message, type = 'info') {
|
|
window.dispatchEvent(new CustomEvent('toast:show', {
|
|
detail: { message, type }
|
|
}));
|
|
}
|
|
|
|
// Basis-Pfad für alle Anwendungen auf dem Server
|
|
const BASE_PATH = '/home/claude-dev';
|
|
|
|
// Farb-Presets für Anwendungen
|
|
const COLOR_PRESETS = [
|
|
'#4F46E5', // Indigo
|
|
'#7C3AED', // Violet
|
|
'#EC4899', // Pink
|
|
'#EF4444', // Red
|
|
'#F59E0B', // Amber
|
|
'#10B981', // Emerald
|
|
'#06B6D4', // Cyan
|
|
'#3B82F6', // Blue
|
|
'#8B5CF6', // Purple
|
|
'#6366F1' // Indigo Light
|
|
];
|
|
|
|
class CodingManager {
|
|
constructor() {
|
|
this.initialized = false;
|
|
this.directories = [];
|
|
this.refreshInterval = null;
|
|
this.editingDirectory = null;
|
|
this.giteaRepos = [];
|
|
}
|
|
|
|
/**
|
|
* Manager initialisieren
|
|
*/
|
|
async init() {
|
|
if (this.initialized) return;
|
|
|
|
this.bindEvents();
|
|
this.initialized = true;
|
|
|
|
console.log('[CodingManager] Initialisiert');
|
|
}
|
|
|
|
/**
|
|
* Event-Listener binden
|
|
*/
|
|
bindEvents() {
|
|
// Add-Button
|
|
const addBtn = document.getElementById('add-coding-directory-btn');
|
|
if (addBtn) {
|
|
addBtn.addEventListener('click', () => this.openModal());
|
|
}
|
|
|
|
// Modal Events
|
|
const modal = document.getElementById('coding-modal');
|
|
if (modal) {
|
|
// Close-Button
|
|
modal.querySelector('.modal-close')?.addEventListener('click', () => this.closeModal());
|
|
modal.querySelector('.modal-cancel')?.addEventListener('click', () => this.closeModal());
|
|
|
|
// Save-Button
|
|
document.getElementById('coding-save-btn')?.addEventListener('click', () => this.handleSave());
|
|
|
|
// Delete-Button
|
|
document.getElementById('coding-delete-btn')?.addEventListener('click', () => this.handleDelete());
|
|
|
|
// Backdrop-Click
|
|
modal.addEventListener('click', (e) => {
|
|
if (e.target === modal) this.closeModal();
|
|
});
|
|
|
|
// Farb-Presets
|
|
this.renderColorPresets();
|
|
|
|
// Name-Eingabe für Pfad-Preview
|
|
const nameInput = document.getElementById('coding-name');
|
|
if (nameInput) {
|
|
nameInput.addEventListener('input', () => this.updatePathPreview());
|
|
}
|
|
|
|
// CLAUDE.md Link Event
|
|
const claudeLink = document.getElementById('coding-claude-link');
|
|
if (claudeLink) {
|
|
claudeLink.addEventListener('click', () => this.openClaudeModal());
|
|
}
|
|
}
|
|
|
|
// Command-Modal Events
|
|
const cmdModal = document.getElementById('coding-command-modal');
|
|
if (cmdModal) {
|
|
cmdModal.querySelector('.modal-close')?.addEventListener('click', () => this.closeCommandModal());
|
|
cmdModal.addEventListener('click', (e) => {
|
|
if (e.target === cmdModal) this.closeCommandModal();
|
|
});
|
|
|
|
document.getElementById('coding-copy-command')?.addEventListener('click', () => this.copyCommand());
|
|
}
|
|
|
|
// Gitea-Repo Dropdown laden bei Details-Toggle
|
|
const giteaSection = document.querySelector('.coding-gitea-section');
|
|
if (giteaSection) {
|
|
giteaSection.addEventListener('toggle', (e) => {
|
|
if (e.target.open) {
|
|
this.loadGiteaRepos();
|
|
}
|
|
});
|
|
}
|
|
|
|
// CLAUDE.md Modal Events
|
|
const claudeModal = document.getElementById('claude-md-modal');
|
|
if (claudeModal) {
|
|
// Close-Button
|
|
claudeModal.querySelector('.modal-close')?.addEventListener('click', () => this.closeClaudeModal());
|
|
|
|
// Backdrop-Click
|
|
claudeModal.addEventListener('click', (e) => {
|
|
if (e.target === claudeModal) this.closeClaudeModal();
|
|
});
|
|
|
|
// ESC-Taste
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Escape' && !claudeModal.classList.contains('hidden')) {
|
|
this.closeClaudeModal();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Farb-Presets rendern
|
|
*/
|
|
renderColorPresets() {
|
|
const container = document.getElementById('coding-color-presets');
|
|
if (!container) return;
|
|
|
|
container.innerHTML = COLOR_PRESETS.map(color => `
|
|
<button type="button" class="color-preset" data-color="${color}" style="background-color: ${color};" title="${color}"></button>
|
|
`).join('') + `
|
|
<input type="color" id="coding-color-custom" class="color-picker-custom" value="#4F46E5" title="Eigene Farbe">
|
|
`;
|
|
|
|
// Event-Listener für Presets
|
|
container.querySelectorAll('.color-preset').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
container.querySelectorAll('.color-preset').forEach(b => b.classList.remove('selected'));
|
|
btn.classList.add('selected');
|
|
document.getElementById('coding-color-custom').value = btn.dataset.color;
|
|
});
|
|
});
|
|
|
|
// Custom Color Input
|
|
document.getElementById('coding-color-custom')?.addEventListener('input', (e) => {
|
|
container.querySelectorAll('.color-preset').forEach(b => b.classList.remove('selected'));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Pfad-Preview aktualisieren
|
|
*/
|
|
updatePathPreview() {
|
|
const nameInput = document.getElementById('coding-name');
|
|
const preview = document.getElementById('coding-path-preview');
|
|
if (!nameInput || !preview) return;
|
|
|
|
const name = nameInput.value.trim();
|
|
preview.textContent = name || '...';
|
|
}
|
|
|
|
// switchClaudeTab entfernt - CLAUDE.md ist jetzt nur readonly
|
|
|
|
/**
|
|
* CLAUDE.md Link aktualisieren
|
|
*/
|
|
updateClaudeLink(content, projectName) {
|
|
const link = document.getElementById('coding-claude-link');
|
|
const textSpan = link?.querySelector('.claude-text');
|
|
|
|
// Debug entfernt
|
|
|
|
if (!link || !textSpan) {
|
|
console.error('CLAUDE.md link elements not found!');
|
|
return;
|
|
}
|
|
|
|
// Content für Modal speichern
|
|
this.currentClaudeContent = content;
|
|
this.currentProjectName = projectName;
|
|
|
|
if (content) {
|
|
link.disabled = false;
|
|
textSpan.textContent = `CLAUDE.md anzeigen (${Math.round(content.length / 1024)}KB)`;
|
|
} else {
|
|
link.disabled = true;
|
|
textSpan.textContent = 'Keine CLAUDE.md vorhanden';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* CLAUDE.md Modal öffnen
|
|
*/
|
|
openClaudeModal() {
|
|
if (!this.currentClaudeContent) {
|
|
console.warn('No CLAUDE.md content to display');
|
|
return;
|
|
}
|
|
|
|
const modal = document.getElementById('claude-md-modal');
|
|
const overlay = document.querySelector('.modal-overlay');
|
|
const content = document.getElementById('claude-md-content');
|
|
const title = modal?.querySelector('.modal-header h3');
|
|
|
|
if (!modal || !content) {
|
|
console.error('CLAUDE.md modal elements not found!');
|
|
return;
|
|
}
|
|
|
|
// Titel setzen
|
|
if (title && this.currentProjectName) {
|
|
title.textContent = `CLAUDE.md - ${this.currentProjectName}`;
|
|
}
|
|
|
|
// Content setzen
|
|
content.textContent = this.currentClaudeContent;
|
|
|
|
// Modal anzeigen
|
|
modal.classList.remove('hidden');
|
|
modal.classList.add('visible');
|
|
if (overlay) {
|
|
overlay.classList.remove('hidden');
|
|
overlay.classList.add('visible');
|
|
}
|
|
|
|
// Modal opened
|
|
}
|
|
|
|
/**
|
|
* CLAUDE.md Modal schließen
|
|
*/
|
|
closeClaudeModal() {
|
|
const modal = document.getElementById('claude-md-modal');
|
|
const overlay = document.querySelector('.modal-overlay');
|
|
|
|
if (modal) {
|
|
modal.classList.remove('visible');
|
|
setTimeout(() => modal.classList.add('hidden'), 200);
|
|
}
|
|
|
|
if (overlay) {
|
|
overlay.classList.remove('visible');
|
|
setTimeout(() => overlay.classList.add('hidden'), 200);
|
|
}
|
|
|
|
// Modal closed
|
|
}
|
|
|
|
/**
|
|
* Gitea-Repositories laden
|
|
*/
|
|
async loadGiteaRepos() {
|
|
try {
|
|
const select = document.getElementById('coding-gitea-repo');
|
|
if (!select) return;
|
|
|
|
// Lade-Indikator
|
|
select.innerHTML = '<option value="">Laden...</option>';
|
|
|
|
const result = await api.getGiteaRepositories();
|
|
console.log('Gitea API Response:', result);
|
|
this.giteaRepos = result?.repositories || [];
|
|
|
|
select.innerHTML = '<option value="">-- Kein Repository --</option>' +
|
|
this.giteaRepos.map(repo => `
|
|
<option value="${repo.cloneUrl}" data-owner="${repo.owner || ''}" data-name="${repo.name}">
|
|
${escapeHtml(repo.fullName)}
|
|
</option>
|
|
`).join('');
|
|
|
|
// Wenn Editing, vorhandenen Wert setzen
|
|
if (this.editingDirectory?.giteaRepoUrl) {
|
|
select.value = this.editingDirectory.giteaRepoUrl;
|
|
}
|
|
} catch (error) {
|
|
console.error('Fehler beim Laden der Gitea-Repos:', error);
|
|
const select = document.getElementById('coding-gitea-repo');
|
|
if (select) {
|
|
select.innerHTML = '<option value="">Fehler beim Laden</option>';
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Anwendungen laden
|
|
*/
|
|
async loadDirectories() {
|
|
try {
|
|
this.directories = await api.getCodingDirectories();
|
|
this.render();
|
|
} catch (error) {
|
|
console.error('Fehler beim Laden der Anwendungen:', error);
|
|
showToast('Fehler beim Laden der Anwendungen', 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* View rendern
|
|
*/
|
|
render() {
|
|
const grid = document.getElementById('coding-grid');
|
|
const empty = document.getElementById('coding-empty');
|
|
|
|
if (!grid) return;
|
|
|
|
if (this.directories.length === 0) {
|
|
grid.innerHTML = '';
|
|
grid.classList.add('hidden');
|
|
empty?.classList.remove('hidden');
|
|
return;
|
|
}
|
|
|
|
empty?.classList.add('hidden');
|
|
grid.classList.remove('hidden');
|
|
|
|
grid.innerHTML = this.directories.map(dir => this.renderTile(dir)).join('');
|
|
|
|
// Event-Listener für Tiles
|
|
this.bindTileEvents();
|
|
|
|
// Git-Status für jede Anwendung laden
|
|
this.directories.forEach(dir => this.updateTileStatus(dir.id));
|
|
}
|
|
|
|
/**
|
|
* Einzelne Kachel rendern
|
|
*/
|
|
renderTile(directory) {
|
|
const hasGitea = !!directory.giteaRepoUrl;
|
|
|
|
return `
|
|
<div class="coding-tile" data-id="${directory.id}">
|
|
<div class="coding-tile-color" style="background-color: ${directory.color || '#4F46E5'}"></div>
|
|
|
|
<div class="coding-tile-header">
|
|
<span class="coding-tile-icon">📁</span>
|
|
</div>
|
|
|
|
<div class="coding-tile-content">
|
|
<div class="coding-tile-name">${escapeHtml(directory.name)}</div>
|
|
<div class="coding-tile-path">${escapeHtml(directory.localPath)}</div>
|
|
${directory.description ? `<div class="coding-tile-description">${escapeHtml(directory.description)}</div>` : ''}
|
|
${directory.hasCLAUDEmd ? '<div class="coding-tile-badge">CLAUDE.md</div>' : ''}
|
|
</div>
|
|
|
|
<div class="coding-tile-status" id="coding-status-${directory.id}">
|
|
<span class="git-status-badge loading">Lade...</span>
|
|
</div>
|
|
|
|
<div class="coding-tile-actions">
|
|
<button class="btn-claude" data-id="${directory.id}" data-path="${escapeHtml(directory.localPath)}" title="SSH-Befehl für Claude kopieren">
|
|
Claude starten
|
|
</button>
|
|
</div>
|
|
|
|
${hasGitea ? `
|
|
<div class="coding-tile-git">
|
|
<button class="btn btn-sm btn-secondary coding-git-fetch" data-id="${directory.id}">Fetch</button>
|
|
<button class="btn btn-sm btn-secondary coding-git-pull" data-id="${directory.id}">Pull</button>
|
|
<button class="btn btn-sm btn-secondary coding-git-push" data-id="${directory.id}">Push</button>
|
|
<button class="btn btn-sm btn-secondary coding-git-commit" data-id="${directory.id}">Commit</button>
|
|
</div>
|
|
` : ''}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* Event-Listener für Tiles binden
|
|
*/
|
|
bindTileEvents() {
|
|
// Kachel-Klick für Modal
|
|
document.querySelectorAll('.coding-tile').forEach(tile => {
|
|
tile.addEventListener('click', (e) => {
|
|
// Nicht triggern wenn Button-Kind geklickt wird
|
|
if (e.target.closest('button')) return;
|
|
|
|
const id = parseInt(tile.dataset.id);
|
|
const dir = this.directories.find(d => d.id === id);
|
|
if (dir) this.openModal(dir);
|
|
});
|
|
});
|
|
|
|
// Claude-Buttons
|
|
document.querySelectorAll('.btn-claude').forEach(btn => {
|
|
btn.addEventListener('click', () => this.launchClaude(btn.dataset.path));
|
|
});
|
|
|
|
// Git-Buttons
|
|
document.querySelectorAll('.coding-git-fetch').forEach(btn => {
|
|
btn.addEventListener('click', () => this.gitFetch(parseInt(btn.dataset.id)));
|
|
});
|
|
|
|
document.querySelectorAll('.coding-git-pull').forEach(btn => {
|
|
btn.addEventListener('click', () => this.gitPull(parseInt(btn.dataset.id)));
|
|
});
|
|
|
|
document.querySelectorAll('.coding-git-push').forEach(btn => {
|
|
btn.addEventListener('click', () => this.gitPush(parseInt(btn.dataset.id)));
|
|
});
|
|
|
|
document.querySelectorAll('.coding-git-commit').forEach(btn => {
|
|
btn.addEventListener('click', () => this.promptCommit(parseInt(btn.dataset.id)));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Git-Status für eine Kachel aktualisieren
|
|
*/
|
|
async updateTileStatus(id) {
|
|
const statusEl = document.getElementById(`coding-status-${id}`);
|
|
if (!statusEl) return;
|
|
|
|
try {
|
|
const status = await api.getCodingDirectoryStatus(id);
|
|
|
|
if (!status.isGitRepo) {
|
|
statusEl.innerHTML = '<span class="git-status-badge">Kein Git-Repo</span>';
|
|
return;
|
|
}
|
|
|
|
const statusClass = status.isClean ? 'clean' : 'dirty';
|
|
const statusText = status.isClean ? 'Clean' : `${status.changes?.length || 0} Änderungen`;
|
|
|
|
statusEl.innerHTML = `
|
|
<span class="git-branch-badge">${escapeHtml(status.branch)}</span>
|
|
<span class="git-status-badge ${statusClass}">${statusText}</span>
|
|
${status.ahead > 0 ? `<span class="git-status-badge ahead">↑${status.ahead}</span>` : ''}
|
|
${status.behind > 0 ? `<span class="git-status-badge behind">↓${status.behind}</span>` : ''}
|
|
`;
|
|
} catch (error) {
|
|
statusEl.innerHTML = '<span class="git-status-badge error">Fehler</span>';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Claude Code starten - SSH-Befehl kopieren
|
|
*/
|
|
async launchClaude(path) {
|
|
const command = `ssh claude-dev@91.99.192.14 -t "cd ${path} && claude"`;
|
|
|
|
try {
|
|
await navigator.clipboard.writeText(command);
|
|
this.showCommandModal(
|
|
command,
|
|
'Befehl kopiert! Öffne Terminal/CMD und füge ein (Strg+V). Passwort: z0E1Al}q2H?Yqd!O'
|
|
);
|
|
showToast('SSH-Befehl kopiert!', 'success');
|
|
} catch (error) {
|
|
// Fallback wenn Clipboard nicht verfügbar
|
|
this.showCommandModal(
|
|
command,
|
|
'Kopiere diesen Befehl und füge ihn im Terminal ein. Passwort: z0E1Al}q2H?Yqd!O'
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Command-Modal anzeigen
|
|
*/
|
|
showCommandModal(command, hint) {
|
|
const modal = document.getElementById('coding-command-modal');
|
|
const hintEl = document.getElementById('coding-command-hint');
|
|
const textEl = document.getElementById('coding-command-text');
|
|
|
|
if (!modal || !textEl) return;
|
|
|
|
hintEl.textContent = hint || 'Führe diesen Befehl aus:';
|
|
textEl.textContent = command;
|
|
this.currentCommand = command;
|
|
|
|
modal.classList.remove('hidden');
|
|
}
|
|
|
|
/**
|
|
* Command-Modal schließen
|
|
*/
|
|
closeCommandModal() {
|
|
const modal = document.getElementById('coding-command-modal');
|
|
if (modal) modal.classList.add('hidden');
|
|
}
|
|
|
|
/**
|
|
* Befehl in Zwischenablage kopieren
|
|
*/
|
|
async copyCommand() {
|
|
if (!this.currentCommand) return;
|
|
|
|
try {
|
|
await navigator.clipboard.writeText(this.currentCommand);
|
|
showToast('Befehl kopiert!', 'success');
|
|
} catch (error) {
|
|
console.error('Kopieren fehlgeschlagen:', error);
|
|
showToast('Kopieren fehlgeschlagen', 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Git Fetch
|
|
*/
|
|
async gitFetch(id) {
|
|
try {
|
|
showToast('Fetch läuft...', 'info');
|
|
await api.codingGitFetch(id);
|
|
showToast('Fetch erfolgreich', 'success');
|
|
this.updateTileStatus(id);
|
|
} catch (error) {
|
|
showToast('Fetch fehlgeschlagen', 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Git Pull
|
|
*/
|
|
async gitPull(id) {
|
|
try {
|
|
showToast('Pull läuft...', 'info');
|
|
await api.codingGitPull(id);
|
|
showToast('Pull erfolgreich', 'success');
|
|
this.updateTileStatus(id);
|
|
} catch (error) {
|
|
showToast('Pull fehlgeschlagen: ' + (error.message || 'Unbekannter Fehler'), 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Git Push
|
|
*/
|
|
async gitPush(id) {
|
|
try {
|
|
showToast('Push läuft...', 'info');
|
|
await api.codingGitPush(id);
|
|
showToast('Push erfolgreich', 'success');
|
|
this.updateTileStatus(id);
|
|
} catch (error) {
|
|
showToast('Push fehlgeschlagen: ' + (error.message || 'Unbekannter Fehler'), 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Commit-Dialog anzeigen
|
|
*/
|
|
promptCommit(id) {
|
|
const message = prompt('Commit-Nachricht eingeben:');
|
|
if (message && message.trim()) {
|
|
this.gitCommit(id, message.trim());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Git Commit
|
|
*/
|
|
async gitCommit(id, message) {
|
|
try {
|
|
showToast('Commit läuft...', 'info');
|
|
await api.codingGitCommit(id, message);
|
|
showToast('Commit erfolgreich', 'success');
|
|
this.updateTileStatus(id);
|
|
} catch (error) {
|
|
showToast('Commit fehlgeschlagen: ' + (error.message || 'Unbekannter Fehler'), 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Modal öffnen
|
|
*/
|
|
openModal(directory = null) {
|
|
this.editingDirectory = directory;
|
|
|
|
const modal = document.getElementById('coding-modal');
|
|
const overlay = document.querySelector('.modal-overlay');
|
|
const title = document.getElementById('coding-modal-title');
|
|
const deleteBtn = document.getElementById('coding-delete-btn');
|
|
|
|
if (!modal) return;
|
|
|
|
// Titel setzen
|
|
title.textContent = directory ? 'Anwendung bearbeiten' : 'Anwendung hinzufügen';
|
|
|
|
// Delete-Button anzeigen/verstecken
|
|
if (deleteBtn) {
|
|
deleteBtn.classList.toggle('hidden', !directory);
|
|
}
|
|
|
|
// Felder füllen
|
|
document.getElementById('coding-name').value = directory?.name || '';
|
|
document.getElementById('coding-description').value = directory?.description || '';
|
|
document.getElementById('coding-branch').value = directory?.defaultBranch || 'main';
|
|
|
|
// CLAUDE.md: Nur aus Dateisystem anzeigen
|
|
const claudeContent = directory?.claudeMdFromDisk || '';
|
|
this.updateClaudeLink(claudeContent, directory?.name);
|
|
|
|
// Pfad-Preview aktualisieren
|
|
this.updatePathPreview();
|
|
|
|
// Farbe setzen
|
|
const color = directory?.color || '#4F46E5';
|
|
document.getElementById('coding-color-custom').value = color;
|
|
document.querySelectorAll('.color-preset').forEach(btn => {
|
|
btn.classList.toggle('selected', btn.dataset.color === color);
|
|
});
|
|
|
|
// Gitea-Sektion zurücksetzen
|
|
const giteaSection = document.querySelector('.coding-gitea-section');
|
|
if (giteaSection) {
|
|
giteaSection.open = !!directory?.giteaRepoUrl;
|
|
}
|
|
|
|
// Repos laden wenn nötig
|
|
if (directory?.giteaRepoUrl) {
|
|
this.loadGiteaRepos();
|
|
}
|
|
|
|
// Modal und Overlay anzeigen
|
|
modal.classList.remove('hidden');
|
|
modal.classList.add('visible');
|
|
if (overlay) {
|
|
overlay.classList.remove('hidden');
|
|
overlay.classList.add('visible');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Modal schließen
|
|
*/
|
|
closeModal() {
|
|
const modal = document.getElementById('coding-modal');
|
|
const overlay = document.querySelector('.modal-overlay');
|
|
|
|
if (modal) {
|
|
modal.classList.remove('visible');
|
|
setTimeout(() => modal.classList.add('hidden'), 200);
|
|
this.editingDirectory = null;
|
|
}
|
|
|
|
if (overlay) {
|
|
overlay.classList.remove('visible');
|
|
setTimeout(() => overlay.classList.add('hidden'), 200);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Speichern-Handler
|
|
*/
|
|
async handleSave() {
|
|
const name = document.getElementById('coding-name').value.trim();
|
|
const description = document.getElementById('coding-description').value.trim();
|
|
// CLAUDE.md wird nicht mehr gespeichert - nur readonly
|
|
const defaultBranch = document.getElementById('coding-branch').value.trim() || 'main';
|
|
|
|
// Pfad automatisch aus Name generieren
|
|
const localPath = `${BASE_PATH}/${name}`;
|
|
|
|
// Farbe ermitteln
|
|
const selectedPreset = document.querySelector('.color-preset.selected');
|
|
const color = selectedPreset?.dataset.color || document.getElementById('coding-color-custom').value;
|
|
|
|
// Gitea-Daten
|
|
const giteaSelect = document.getElementById('coding-gitea-repo');
|
|
const giteaRepoUrl = giteaSelect?.value || null;
|
|
const giteaRepoOwner = giteaSelect?.selectedOptions[0]?.dataset.owner || null;
|
|
const giteaRepoName = giteaSelect?.selectedOptions[0]?.dataset.name || null;
|
|
|
|
if (!name) {
|
|
showToast('Anwendungsname ist erforderlich', 'error');
|
|
return;
|
|
}
|
|
|
|
const data = {
|
|
name,
|
|
localPath,
|
|
description,
|
|
color,
|
|
// claudeInstructions entfernt - CLAUDE.md ist readonly
|
|
giteaRepoUrl,
|
|
giteaRepoOwner,
|
|
giteaRepoName,
|
|
defaultBranch
|
|
};
|
|
|
|
try {
|
|
if (this.editingDirectory) {
|
|
await api.updateCodingDirectory(this.editingDirectory.id, data);
|
|
showToast('Anwendung aktualisiert', 'success');
|
|
} else {
|
|
await api.createCodingDirectory(data);
|
|
showToast('Anwendung hinzugefügt', 'success');
|
|
}
|
|
|
|
this.closeModal();
|
|
await this.loadDirectories();
|
|
} catch (error) {
|
|
console.error('Fehler beim Speichern:', error);
|
|
showToast(error.message || 'Fehler beim Speichern', 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Löschen-Handler
|
|
*/
|
|
async handleDelete() {
|
|
if (!this.editingDirectory) return;
|
|
|
|
if (!confirm(`Anwendung "${this.editingDirectory.name}" wirklich löschen?`)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await api.deleteCodingDirectory(this.editingDirectory.id);
|
|
showToast('Anwendung gelöscht', 'success');
|
|
this.closeModal();
|
|
await this.loadDirectories();
|
|
} catch (error) {
|
|
console.error('Fehler beim Löschen:', error);
|
|
showToast('Fehler beim Löschen', 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Auto-Refresh starten
|
|
*/
|
|
startAutoRefresh() {
|
|
this.stopAutoRefresh();
|
|
// Alle 30 Sekunden aktualisieren
|
|
this.refreshInterval = setInterval(() => {
|
|
this.directories.forEach(dir => this.updateTileStatus(dir.id));
|
|
}, 30000);
|
|
}
|
|
|
|
/**
|
|
* Auto-Refresh stoppen
|
|
*/
|
|
stopAutoRefresh() {
|
|
if (this.refreshInterval) {
|
|
clearInterval(this.refreshInterval);
|
|
this.refreshInterval = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* View anzeigen
|
|
*/
|
|
async show() {
|
|
await this.loadDirectories();
|
|
this.startAutoRefresh();
|
|
}
|
|
|
|
/**
|
|
* View verstecken
|
|
*/
|
|
hide() {
|
|
this.stopAutoRefresh();
|
|
}
|
|
}
|
|
|
|
// Singleton-Instanz erstellen und exportieren
|
|
const codingManager = new CodingManager();
|
|
export default codingManager;
|