Datenbank bereinigt / Gitea-Integration gefixt
Dieser Commit ist enthalten in:
committet von
Server Deploy
Ursprung
395598c2b0
Commit
c21be47428
777
frontend/js/coding.js
Normale Datei
777
frontend/js/coding.js
Normale Datei
@ -0,0 +1,777 @@
|
||||
/**
|
||||
* 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;
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren