1481 Zeilen
45 KiB
JavaScript
1481 Zeilen
45 KiB
JavaScript
/**
|
|
* TASKMATE - Gitea Manager
|
|
* ========================
|
|
* Git-Repository-Verwaltung pro Projekt
|
|
*/
|
|
|
|
import api from './api.js';
|
|
import { $, $$, escapeHtml } from './utils.js';
|
|
import store from './store.js';
|
|
|
|
class GiteaManager {
|
|
constructor() {
|
|
// Gemeinsame Eigenschaften
|
|
this.initialized = false;
|
|
this.refreshInterval = null;
|
|
this.isLoading = false;
|
|
this.currentMode = 'server'; // 'server' oder 'project'
|
|
|
|
// Projekt-Modus Eigenschaften
|
|
this.application = null;
|
|
this.gitStatus = null;
|
|
this.branches = [];
|
|
this.commits = [];
|
|
this.giteaRepos = [];
|
|
this.giteaConnected = false;
|
|
this.hiddenCommits = new Set();
|
|
|
|
// Server-Modus Eigenschaften
|
|
this.serverInfo = null;
|
|
this.serverStatus = null;
|
|
this.serverBranches = [];
|
|
this.serverCommits = [];
|
|
this.hiddenServerCommits = new Set();
|
|
}
|
|
|
|
async init() {
|
|
if (this.initialized) {
|
|
return;
|
|
}
|
|
|
|
// DOM Elements - Gemeinsam
|
|
this.giteaView = $('#view-gitea');
|
|
this.modeSwitch = $('#gitea-mode-switch');
|
|
|
|
// DOM Elements - Server-Modus
|
|
this.serverModeSection = $('#gitea-server-mode');
|
|
|
|
// DOM Elements - Projekt-Modus
|
|
this.noProjectSection = $('#gitea-no-project');
|
|
this.configSection = $('#gitea-config-section');
|
|
this.mainSection = $('#gitea-main-section');
|
|
this.connectionStatus = $('#gitea-connection-status');
|
|
|
|
this.bindEvents();
|
|
this.bindServerEvents();
|
|
this.subscribeToStore();
|
|
this.initialized = true;
|
|
}
|
|
|
|
bindEvents() {
|
|
// Modus-Schalter
|
|
$$('.gitea-mode-btn').forEach(btn => {
|
|
btn.addEventListener('click', (e) => this.handleModeSwitch(e));
|
|
});
|
|
|
|
// Konfiguration speichern
|
|
$('#gitea-config-form')?.addEventListener('submit', (e) => this.handleConfigSave(e));
|
|
|
|
// Repository auswählen
|
|
$('#gitea-repo-select')?.addEventListener('change', (e) => this.handleRepoSelect(e));
|
|
|
|
// Repositories aktualisieren
|
|
$('#btn-refresh-repos')?.addEventListener('click', () => this.loadGiteaRepos());
|
|
|
|
// Neues Repository erstellen
|
|
$('#btn-create-repo')?.addEventListener('click', () => this.openCreateRepoModal());
|
|
|
|
// Git-Operationen
|
|
$('#btn-git-fetch')?.addEventListener('click', () => this.handleFetch());
|
|
$('#btn-git-pull')?.addEventListener('click', () => this.handlePull());
|
|
$('#btn-git-push')?.addEventListener('click', () => this.handlePush());
|
|
$('#btn-git-commit')?.addEventListener('click', () => this.openCommitModal());
|
|
|
|
// Branch wechseln
|
|
$('#branch-select')?.addEventListener('change', (e) => this.handleBranchChange(e));
|
|
|
|
// Pfad validieren
|
|
$('#local-path-input')?.addEventListener('blur', (e) => this.validateLocalPath(e.target.value));
|
|
|
|
// Konfiguration bearbeiten/entfernen
|
|
$('#btn-edit-config')?.addEventListener('click', () => this.showConfigSection());
|
|
$('#btn-remove-config')?.addEventListener('click', () => this.handleRemoveConfig());
|
|
|
|
// Create Repo Modal
|
|
$('#create-repo-form')?.addEventListener('submit', (e) => this.handleCreateRepo(e));
|
|
|
|
// Commit Modal
|
|
$('#git-commit-form')?.addEventListener('submit', (e) => this.handleCommit(e));
|
|
|
|
// Push Modal
|
|
$('#git-push-form')?.addEventListener('submit', (e) => this.executePush(e));
|
|
|
|
// Branch umbenennen
|
|
$('#btn-rename-branch')?.addEventListener('click', () => this.openRenameBranchModal());
|
|
$('#git-rename-branch-form')?.addEventListener('submit', (e) => this.executeRenameBranch(e));
|
|
|
|
// Commits ausblenden
|
|
$('#btn-clear-commits')?.addEventListener('click', () => this.clearAllCommits());
|
|
$('#git-commits-list')?.addEventListener('click', (e) => this.handleCommitListClick(e));
|
|
}
|
|
|
|
bindServerEvents() {
|
|
// Server Git-Operationen
|
|
$('#btn-server-fetch')?.addEventListener('click', () => this.handleServerFetch());
|
|
$('#btn-server-pull')?.addEventListener('click', () => this.handleServerPull());
|
|
$('#btn-server-push')?.addEventListener('click', () => this.openServerPushModal());
|
|
$('#btn-server-commit')?.addEventListener('click', () => this.openServerCommitModal());
|
|
|
|
// Server Branch wechseln
|
|
$('#server-branch-select')?.addEventListener('change', (e) => this.handleServerBranchChange(e));
|
|
|
|
// Server Commits ausblenden
|
|
$('#btn-server-clear-commits')?.addEventListener('click', () => this.clearAllServerCommits());
|
|
$('#server-commits-list')?.addEventListener('click', (e) => this.handleServerCommitListClick(e));
|
|
}
|
|
|
|
// Modus-Wechsel Handler
|
|
handleModeSwitch(e) {
|
|
const btn = e.currentTarget;
|
|
const newMode = btn.dataset.mode;
|
|
|
|
if (newMode === this.currentMode) return;
|
|
|
|
// Button-Status aktualisieren
|
|
$$('.gitea-mode-btn').forEach(b => b.classList.remove('active'));
|
|
btn.classList.add('active');
|
|
|
|
this.currentMode = newMode;
|
|
this.updateModeView();
|
|
}
|
|
|
|
updateModeView() {
|
|
if (this.currentMode === 'server') {
|
|
// Server-Modus
|
|
this.serverModeSection?.classList.remove('hidden');
|
|
this.noProjectSection?.classList.add('hidden');
|
|
this.configSection?.classList.add('hidden');
|
|
this.mainSection?.classList.add('hidden');
|
|
this.loadServerData();
|
|
} else {
|
|
// Projekt-Modus
|
|
this.serverModeSection?.classList.add('hidden');
|
|
this.loadApplication();
|
|
}
|
|
}
|
|
|
|
// =====================
|
|
// SERVER-MODUS METHODEN
|
|
// =====================
|
|
|
|
async loadServerData() {
|
|
this.showLoading();
|
|
|
|
try {
|
|
// Server-Info laden
|
|
const infoResult = await api.getServerGitInfo();
|
|
this.serverInfo = infoResult;
|
|
|
|
if (infoResult.accessible && infoResult.isRepository) {
|
|
// Remote-URL anzeigen
|
|
if (infoResult.remoteUrl) {
|
|
const repoUrl = $('#server-repo-url');
|
|
if (repoUrl) {
|
|
// Gitea HTML URL generieren (ohne .git)
|
|
const htmlUrl = infoResult.remoteUrl.replace(/\.git$/, '');
|
|
repoUrl.href = htmlUrl;
|
|
repoUrl.textContent = htmlUrl;
|
|
repoUrl.classList.remove('hidden');
|
|
}
|
|
}
|
|
|
|
// Git-Daten laden
|
|
await this.loadServerGitData();
|
|
} else {
|
|
this.showToast('Server-Verzeichnis ist kein Git-Repository', 'error');
|
|
}
|
|
} catch (error) {
|
|
console.error('[Gitea Server] Fehler beim Laden:', error);
|
|
this.showToast('Fehler beim Laden der Server-Daten', 'error');
|
|
}
|
|
}
|
|
|
|
async loadServerGitData() {
|
|
try {
|
|
const [statusResult, branchesResult, commitsResult] = await Promise.allSettled([
|
|
api.getServerGitStatus(),
|
|
api.getServerGitBranches(),
|
|
api.getServerGitCommits(10)
|
|
]);
|
|
|
|
if (statusResult.status === 'fulfilled') {
|
|
this.serverStatus = statusResult.value;
|
|
} else {
|
|
this.serverStatus = null;
|
|
console.error('[Gitea Server] Status-Fehler:', statusResult.reason);
|
|
}
|
|
|
|
if (branchesResult.status === 'fulfilled') {
|
|
this.serverBranches = branchesResult.value.branches || [];
|
|
} else {
|
|
this.serverBranches = [];
|
|
}
|
|
|
|
if (commitsResult.status === 'fulfilled') {
|
|
this.serverCommits = commitsResult.value.commits || [];
|
|
} else {
|
|
this.serverCommits = [];
|
|
}
|
|
|
|
this.renderServerStatus();
|
|
this.renderServerBranches();
|
|
this.renderServerCommits();
|
|
this.renderServerChanges();
|
|
} catch (error) {
|
|
console.error('[Gitea Server] Git-Daten laden fehlgeschlagen:', error);
|
|
}
|
|
}
|
|
|
|
renderServerStatus() {
|
|
const statusBadge = $('#server-status-indicator');
|
|
const changesCount = $('#server-changes-count');
|
|
|
|
if (!this.serverStatus) {
|
|
if (statusBadge) {
|
|
statusBadge.textContent = 'Fehler';
|
|
statusBadge.className = 'status-badge error';
|
|
}
|
|
if (changesCount) changesCount.textContent = '-';
|
|
return;
|
|
}
|
|
|
|
if (statusBadge) {
|
|
if (!this.serverStatus.success) {
|
|
statusBadge.textContent = 'Fehler';
|
|
statusBadge.className = 'status-badge error';
|
|
} else if (this.serverStatus.isClean) {
|
|
statusBadge.textContent = 'Sauber';
|
|
statusBadge.className = 'status-badge clean';
|
|
} else if (this.serverStatus.hasChanges) {
|
|
statusBadge.textContent = 'Geändert';
|
|
statusBadge.className = 'status-badge dirty';
|
|
} else {
|
|
statusBadge.textContent = 'OK';
|
|
statusBadge.className = 'status-badge clean';
|
|
}
|
|
}
|
|
|
|
const changes = this.serverStatus.changes || [];
|
|
if (changesCount) changesCount.textContent = changes.length;
|
|
}
|
|
|
|
renderServerBranches() {
|
|
const select = $('#server-branch-select');
|
|
if (!select) return;
|
|
|
|
const currentBranch = this.serverStatus?.branch || 'main';
|
|
|
|
select.innerHTML = '';
|
|
|
|
const localBranches = this.serverBranches.filter(b => !b.isRemote);
|
|
localBranches.forEach(branch => {
|
|
const option = document.createElement('option');
|
|
option.value = branch.name;
|
|
option.textContent = branch.name;
|
|
if (branch.name === currentBranch) {
|
|
option.selected = true;
|
|
}
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
|
|
renderServerCommits() {
|
|
const listEl = $('#server-commits-list');
|
|
const clearBtn = $('#btn-server-clear-commits');
|
|
if (!listEl) return;
|
|
|
|
const visibleCommits = this.serverCommits.filter(commit => {
|
|
const hash = commit.hash || commit.sha || commit.shortHash;
|
|
return !this.hiddenServerCommits.has(hash);
|
|
});
|
|
|
|
if (clearBtn) {
|
|
clearBtn.style.display = visibleCommits.length > 0 ? '' : 'none';
|
|
}
|
|
|
|
if (visibleCommits.length === 0) {
|
|
const message = this.serverCommits.length > 0
|
|
? 'Alle Commits ausgeblendet'
|
|
: 'Keine Commits gefunden';
|
|
listEl.innerHTML = `<div class="gitea-empty-state" style="padding: var(--spacing-4);"><p>${message}</p></div>`;
|
|
return;
|
|
}
|
|
|
|
listEl.innerHTML = visibleCommits.map(commit => {
|
|
const hash = commit.hash || commit.sha || '';
|
|
const shortHash = commit.shortHash || hash.substring(0, 7);
|
|
return `
|
|
<div class="commit-item" data-hash="${escapeHtml(hash)}">
|
|
<span class="commit-hash">${escapeHtml(shortHash)}</span>
|
|
<div class="commit-info">
|
|
<div class="commit-message">${escapeHtml(commit.message?.split('\n')[0] || '')}</div>
|
|
<div class="commit-meta">
|
|
<span class="author">${escapeHtml(commit.author)}</span> · ${this.formatDate(commit.date)}
|
|
</div>
|
|
</div>
|
|
<button class="commit-delete" title="Ausblenden" data-hash="${escapeHtml(hash)}">
|
|
<svg viewBox="0 0 24 24" width="16" height="16"><path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
|
</button>
|
|
</div>
|
|
`}).join('');
|
|
}
|
|
|
|
renderServerChanges() {
|
|
const changesSection = $('#server-changes-section');
|
|
const listEl = $('#server-changes-list');
|
|
|
|
if (!changesSection || !listEl) return;
|
|
|
|
const changes = this.serverStatus?.changes || [];
|
|
|
|
if (changes.length === 0) {
|
|
changesSection.classList.add('hidden');
|
|
return;
|
|
}
|
|
|
|
changesSection.classList.remove('hidden');
|
|
|
|
listEl.innerHTML = changes.map(change => {
|
|
const statusClass = this.getChangeStatusClass(change.status);
|
|
const statusLabel = this.getChangeStatusLabel(change.status);
|
|
|
|
return `
|
|
<div class="change-item">
|
|
<span class="change-status ${statusClass}" title="${statusLabel}">${change.status}</span>
|
|
<span class="change-file">${escapeHtml(change.file)}</span>
|
|
</div>
|
|
`;
|
|
}).join('');
|
|
}
|
|
|
|
// Server Git-Operationen
|
|
async handleServerFetch() {
|
|
this.setServerOperationLoading('fetch', true);
|
|
|
|
try {
|
|
const result = await api.serverGitFetch();
|
|
if (result.success) {
|
|
this.showToast('Fetch erfolgreich', 'success');
|
|
await this.loadServerGitData();
|
|
} else {
|
|
this.showToast(result.error || 'Fetch fehlgeschlagen', 'error');
|
|
}
|
|
} catch (error) {
|
|
this.showToast(error.message || 'Fetch fehlgeschlagen', 'error');
|
|
} finally {
|
|
this.setServerOperationLoading('fetch', false);
|
|
}
|
|
}
|
|
|
|
async handleServerPull() {
|
|
this.setServerOperationLoading('pull', true);
|
|
|
|
try {
|
|
const branch = $('#server-branch-select')?.value || null;
|
|
const result = await api.serverGitPull(branch);
|
|
|
|
if (result.success) {
|
|
this.showToast('Pull erfolgreich', 'success');
|
|
await this.loadServerGitData();
|
|
} else {
|
|
this.showToast(result.error || 'Pull fehlgeschlagen', 'error');
|
|
}
|
|
} catch (error) {
|
|
this.showToast(error.message || 'Pull fehlgeschlagen', 'error');
|
|
} finally {
|
|
this.setServerOperationLoading('pull', false);
|
|
}
|
|
}
|
|
|
|
openServerPushModal() {
|
|
const modal = $('#git-push-modal');
|
|
if (!modal) return;
|
|
|
|
const currentBranch = $('#server-branch-select')?.value || this.serverStatus?.branch || 'main';
|
|
$('#push-local-branch').textContent = currentBranch;
|
|
$('#push-target-branch').value = '';
|
|
$('#push-force').checked = false;
|
|
|
|
// Temporär Attribut setzen um Server-Modus zu markieren
|
|
modal.dataset.serverMode = 'true';
|
|
|
|
modal.classList.remove('hidden');
|
|
modal.classList.add('visible');
|
|
$('#modal-overlay')?.classList.remove('hidden');
|
|
|
|
store.openModal('git-push-modal');
|
|
}
|
|
|
|
async executeServerPush(force = false) {
|
|
this.setServerOperationLoading('push', true);
|
|
|
|
try {
|
|
const targetBranch = $('#push-target-branch')?.value || null;
|
|
const localBranch = $('#server-branch-select')?.value || this.serverStatus?.branch || 'main';
|
|
|
|
const result = await api.serverGitPush(targetBranch || localBranch, force);
|
|
|
|
if (result.success) {
|
|
const pushedBranch = result.branch || targetBranch || localBranch;
|
|
this.showToast(`Push erfolgreich nach Branch "${pushedBranch}"`, 'success');
|
|
await this.loadServerGitData();
|
|
} else {
|
|
this.showToast(result.error || 'Push fehlgeschlagen', 'error');
|
|
}
|
|
} catch (error) {
|
|
this.showToast(error.message || 'Push fehlgeschlagen', 'error');
|
|
} finally {
|
|
this.setServerOperationLoading('push', false);
|
|
}
|
|
}
|
|
|
|
openServerCommitModal() {
|
|
const modal = $('#git-commit-modal');
|
|
if (!modal) return;
|
|
|
|
$('#commit-message').value = '';
|
|
$('#commit-stage-all').checked = true;
|
|
|
|
// Temporär Attribut setzen um Server-Modus zu markieren
|
|
modal.dataset.serverMode = 'true';
|
|
|
|
modal.classList.remove('hidden');
|
|
modal.classList.add('visible');
|
|
$('#modal-overlay')?.classList.remove('hidden');
|
|
|
|
store.openModal('git-commit-modal');
|
|
}
|
|
|
|
async executeServerCommit(message, stageAll = true) {
|
|
try {
|
|
const result = await api.serverGitCommit(message, stageAll);
|
|
|
|
if (result.success) {
|
|
this.showToast('Commit erstellt', 'success');
|
|
await this.loadServerGitData();
|
|
return true;
|
|
} else {
|
|
this.showToast(result.error || 'Commit fehlgeschlagen', 'error');
|
|
return false;
|
|
}
|
|
} catch (error) {
|
|
this.showToast(error.message || 'Commit fehlgeschlagen', 'error');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async handleServerBranchChange(e) {
|
|
const branch = e.target.value;
|
|
if (!branch) return;
|
|
|
|
try {
|
|
const result = await api.serverGitCheckout(branch);
|
|
|
|
if (result.success) {
|
|
this.showToast(`Gewechselt zu ${branch}`, 'success');
|
|
await this.loadServerGitData();
|
|
} else {
|
|
this.showToast(result.error || 'Branch-Wechsel fehlgeschlagen', 'error');
|
|
await this.loadServerGitData();
|
|
}
|
|
} catch (error) {
|
|
this.showToast(error.message || 'Branch-Wechsel fehlgeschlagen', 'error');
|
|
await this.loadServerGitData();
|
|
}
|
|
}
|
|
|
|
handleServerCommitListClick(e) {
|
|
const deleteBtn = e.target.closest('.commit-delete');
|
|
if (deleteBtn) {
|
|
const hash = deleteBtn.dataset.hash;
|
|
if (hash) {
|
|
this.hideServerCommit(hash);
|
|
}
|
|
}
|
|
}
|
|
|
|
hideServerCommit(hash) {
|
|
this.hiddenServerCommits.add(hash);
|
|
this.renderServerCommits();
|
|
this.showToast('Commit ausgeblendet', 'info');
|
|
}
|
|
|
|
clearAllServerCommits() {
|
|
this.serverCommits.forEach(commit => {
|
|
const hash = commit.hash || commit.sha || commit.shortHash;
|
|
if (hash) {
|
|
this.hiddenServerCommits.add(hash);
|
|
}
|
|
});
|
|
this.renderServerCommits();
|
|
this.showToast('Alle Commits ausgeblendet', 'info');
|
|
}
|
|
|
|
setServerOperationLoading(operation, loading) {
|
|
const buttonId = `btn-server-${operation}`;
|
|
const button = $(`#${buttonId}`);
|
|
if (button) {
|
|
button.disabled = loading;
|
|
if (loading) {
|
|
button.classList.add('loading');
|
|
} else {
|
|
button.classList.remove('loading');
|
|
}
|
|
}
|
|
}
|
|
|
|
// =====================
|
|
// PROJEKT-MODUS METHODEN
|
|
// =====================
|
|
|
|
subscribeToStore() {
|
|
// Bei Projektwechsel neu laden
|
|
store.subscribe('currentProjectId', async (projectId) => {
|
|
if (projectId && store.get('currentView') === 'gitea') {
|
|
await this.loadApplication();
|
|
}
|
|
});
|
|
}
|
|
|
|
async loadApplication() {
|
|
const projectId = store.get('currentProjectId');
|
|
|
|
if (!projectId) {
|
|
this.showNoProjectMessage();
|
|
return;
|
|
}
|
|
|
|
this.showLoading();
|
|
|
|
try {
|
|
const result = await api.getProjectApplication(projectId);
|
|
this.application = result;
|
|
|
|
if (result.configured) {
|
|
await this.loadGitData();
|
|
this.renderConfiguredView();
|
|
} else {
|
|
await this.loadGiteaRepos();
|
|
this.renderConfigurationView();
|
|
}
|
|
} catch (error) {
|
|
console.error('[Gitea] Fehler beim Laden:', error);
|
|
this.showError('Fehler beim Laden der Konfiguration');
|
|
}
|
|
}
|
|
|
|
async loadGitData() {
|
|
const projectId = store.get('currentProjectId');
|
|
|
|
try {
|
|
const [statusResult, branchesResult, commitsResult] = await Promise.allSettled([
|
|
api.getGitStatus(projectId),
|
|
api.getGitBranches(projectId),
|
|
api.getGitCommits(projectId, 10)
|
|
]);
|
|
|
|
if (statusResult.status === 'fulfilled') {
|
|
this.gitStatus = statusResult.value;
|
|
} else {
|
|
this.gitStatus = null;
|
|
console.error('[Gitea] Status-Fehler:', statusResult.reason);
|
|
}
|
|
|
|
if (branchesResult.status === 'fulfilled') {
|
|
this.branches = branchesResult.value.branches || [];
|
|
} else {
|
|
this.branches = [];
|
|
}
|
|
|
|
if (commitsResult.status === 'fulfilled') {
|
|
this.commits = commitsResult.value.commits || [];
|
|
} else {
|
|
this.commits = [];
|
|
}
|
|
|
|
this.renderStatus();
|
|
this.renderBranches();
|
|
this.renderCommits();
|
|
this.renderChanges();
|
|
} catch (error) {
|
|
console.error('[Gitea] Git-Daten laden fehlgeschlagen:', error);
|
|
}
|
|
}
|
|
|
|
async loadGiteaRepos() {
|
|
try {
|
|
const result = await api.testGiteaConnection();
|
|
this.giteaConnected = result.connected;
|
|
this.updateConnectionStatus(result);
|
|
|
|
if (this.giteaConnected) {
|
|
const reposResult = await api.getGiteaRepositories();
|
|
this.giteaRepos = reposResult.repositories || [];
|
|
this.populateRepoSelect();
|
|
}
|
|
} catch (error) {
|
|
this.giteaConnected = false;
|
|
this.updateConnectionStatus({ connected: false, error: error.message });
|
|
console.error('[Gitea] Verbindung fehlgeschlagen:', error);
|
|
}
|
|
}
|
|
|
|
updateConnectionStatus(result) {
|
|
const statusEl = this.connectionStatus;
|
|
if (!statusEl) return;
|
|
|
|
statusEl.classList.remove('connected', 'disconnected');
|
|
|
|
if (result.connected) {
|
|
statusEl.classList.add('connected');
|
|
statusEl.querySelector('.status-text').textContent =
|
|
`Verbunden als ${result.user?.login || 'Benutzer'}`;
|
|
} else {
|
|
statusEl.classList.add('disconnected');
|
|
statusEl.querySelector('.status-text').textContent =
|
|
result.error || 'Verbindung fehlgeschlagen';
|
|
}
|
|
}
|
|
|
|
populateRepoSelect() {
|
|
const select = $('#gitea-repo-select');
|
|
if (!select) return;
|
|
|
|
select.innerHTML = '<option value="">-- Repository wählen --</option>';
|
|
|
|
this.giteaRepos.forEach(repo => {
|
|
const option = document.createElement('option');
|
|
option.value = JSON.stringify({
|
|
url: repo.cloneUrl,
|
|
owner: repo.owner,
|
|
name: repo.name,
|
|
fullName: repo.fullName,
|
|
htmlUrl: repo.htmlUrl,
|
|
defaultBranch: repo.defaultBranch
|
|
});
|
|
option.textContent = repo.fullName;
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
|
|
handleRepoSelect(e) {
|
|
const value = e.target.value;
|
|
if (!value) return;
|
|
|
|
try {
|
|
const repo = JSON.parse(value);
|
|
$('#default-branch-input').value = repo.defaultBranch || 'main';
|
|
} catch (error) {
|
|
console.error('[Gitea] Fehler beim Parsen des Repository:', error);
|
|
}
|
|
}
|
|
|
|
async validateLocalPath(path) {
|
|
const resultEl = $('#path-validation-result');
|
|
if (!resultEl || !path) {
|
|
if (resultEl) {
|
|
resultEl.textContent = '';
|
|
resultEl.className = 'form-hint';
|
|
}
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const result = await api.validatePath(path);
|
|
|
|
if (result.valid) {
|
|
if (result.isRepository) {
|
|
resultEl.textContent = 'Pfad ist ein Git-Repository';
|
|
resultEl.className = 'form-hint success';
|
|
} else {
|
|
resultEl.textContent = 'Pfad ist erreichbar (kein Git-Repository)';
|
|
resultEl.className = 'form-hint';
|
|
}
|
|
} else {
|
|
resultEl.textContent = 'Pfad nicht erreichbar';
|
|
resultEl.className = 'form-hint error';
|
|
}
|
|
} catch (error) {
|
|
resultEl.textContent = 'Fehler bei der Validierung';
|
|
resultEl.className = 'form-hint error';
|
|
}
|
|
}
|
|
|
|
async handleConfigSave(e) {
|
|
e.preventDefault();
|
|
|
|
const projectId = store.get('currentProjectId');
|
|
if (!projectId) return;
|
|
|
|
const repoSelectValue = $('#gitea-repo-select').value;
|
|
const localPath = $('#local-path-input').value.trim();
|
|
const defaultBranch = $('#default-branch-input').value.trim() || 'main';
|
|
|
|
if (!localPath) {
|
|
this.showToast('Bitte geben Sie einen lokalen Pfad an', 'error');
|
|
return;
|
|
}
|
|
|
|
let giteaRepoUrl = null;
|
|
let giteaRepoOwner = null;
|
|
let giteaRepoName = null;
|
|
let cloneUrl = null;
|
|
|
|
if (repoSelectValue) {
|
|
try {
|
|
const repo = JSON.parse(repoSelectValue);
|
|
giteaRepoUrl = repo.htmlUrl;
|
|
giteaRepoOwner = repo.owner;
|
|
giteaRepoName = repo.name;
|
|
cloneUrl = repo.url; // Clone URL für git remote
|
|
} catch (error) {
|
|
console.error('[Gitea] Fehler beim Parsen des Repository:', error);
|
|
}
|
|
}
|
|
|
|
try {
|
|
// 1. Konfiguration speichern
|
|
const result = await api.saveProjectApplication({
|
|
projectId,
|
|
localPath,
|
|
giteaRepoUrl,
|
|
giteaRepoOwner,
|
|
giteaRepoName,
|
|
defaultBranch
|
|
});
|
|
|
|
if (result.success) {
|
|
// 2. Repository für Gitea vorbereiten (init, remote setzen)
|
|
if (cloneUrl) {
|
|
this.showToast('Repository wird vorbereitet...', 'info');
|
|
const prepareResult = await api.prepareRepository(projectId, cloneUrl, defaultBranch);
|
|
|
|
if (prepareResult.success) {
|
|
this.showToast('Konfiguration gespeichert und Repository vorbereitet', 'success');
|
|
} else {
|
|
this.showToast('Konfiguration gespeichert, aber Repository-Vorbereitung fehlgeschlagen: ' + (prepareResult.error || 'Unbekannter Fehler'), 'warning');
|
|
}
|
|
} else {
|
|
this.showToast('Konfiguration gespeichert', 'success');
|
|
}
|
|
|
|
await this.loadApplication();
|
|
} else {
|
|
this.showToast(result.error || 'Fehler beim Speichern', 'error');
|
|
}
|
|
} catch (error) {
|
|
this.showToast(error.message || 'Fehler beim Speichern', 'error');
|
|
}
|
|
}
|
|
|
|
async handleRemoveConfig() {
|
|
const projectId = store.get('currentProjectId');
|
|
if (!projectId) return;
|
|
|
|
if (!confirm('Möchten Sie die Repository-Konfiguration wirklich entfernen?')) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await api.deleteProjectApplication(projectId);
|
|
this.showToast('Konfiguration entfernt', 'success');
|
|
await this.loadApplication();
|
|
} catch (error) {
|
|
this.showToast(error.message || 'Fehler beim Entfernen', 'error');
|
|
}
|
|
}
|
|
|
|
showConfigSection() {
|
|
this.hideAllSections();
|
|
this.configSection?.classList.remove('hidden');
|
|
|
|
// Formular mit aktuellen Werten füllen
|
|
if (this.application?.configured) {
|
|
$('#local-path-input').value = this.application.local_path || '';
|
|
$('#default-branch-input').value = this.application.default_branch || 'main';
|
|
|
|
// Repository im Dropdown auswählen falls vorhanden
|
|
if (this.application.gitea_repo_url) {
|
|
const select = $('#gitea-repo-select');
|
|
const options = select?.querySelectorAll('option');
|
|
options?.forEach(option => {
|
|
if (option.value) {
|
|
try {
|
|
const repo = JSON.parse(option.value);
|
|
if (repo.owner === this.application.gitea_repo_owner &&
|
|
repo.name === this.application.gitea_repo_name) {
|
|
select.value = option.value;
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
this.loadGiteaRepos();
|
|
}
|
|
|
|
// Git Operations
|
|
async handleFetch() {
|
|
const projectId = store.get('currentProjectId');
|
|
if (!projectId) return;
|
|
|
|
this.setOperationLoading('fetch', true);
|
|
|
|
try {
|
|
const result = await api.gitFetch(projectId);
|
|
if (result.success) {
|
|
this.showToast('Fetch erfolgreich', 'success');
|
|
await this.loadGitData();
|
|
} else {
|
|
this.showToast(result.error || 'Fetch fehlgeschlagen', 'error');
|
|
}
|
|
} catch (error) {
|
|
this.showToast(error.message || 'Fetch fehlgeschlagen', 'error');
|
|
} finally {
|
|
this.setOperationLoading('fetch', false);
|
|
}
|
|
}
|
|
|
|
async handlePull() {
|
|
const projectId = store.get('currentProjectId');
|
|
if (!projectId) return;
|
|
|
|
this.setOperationLoading('pull', true);
|
|
|
|
try {
|
|
const branch = $('#branch-select')?.value || null;
|
|
const result = await api.gitPull(projectId, branch);
|
|
|
|
if (result.success) {
|
|
this.showToast('Pull erfolgreich', 'success');
|
|
await this.loadGitData();
|
|
} else {
|
|
this.showToast(result.error || 'Pull fehlgeschlagen', 'error');
|
|
}
|
|
} catch (error) {
|
|
this.showToast(error.message || 'Pull fehlgeschlagen', 'error');
|
|
} finally {
|
|
this.setOperationLoading('pull', false);
|
|
}
|
|
}
|
|
|
|
handlePush() {
|
|
// Öffne das Push-Modal zur Branch-Auswahl
|
|
this.openPushModal();
|
|
}
|
|
|
|
openPushModal() {
|
|
const modal = $('#git-push-modal');
|
|
if (!modal) return;
|
|
|
|
// Aktuellen lokalen Branch anzeigen
|
|
const currentBranch = $('#branch-select')?.value || this.gitStatus?.branch || 'unbekannt';
|
|
$('#push-local-branch').textContent = currentBranch;
|
|
|
|
// Ziel-Branch zurücksetzen (Standard: gleicher Name wie lokal)
|
|
$('#push-target-branch').value = '';
|
|
|
|
modal.classList.remove('hidden');
|
|
modal.classList.add('visible');
|
|
$('#modal-overlay')?.classList.remove('hidden');
|
|
|
|
store.openModal('git-push-modal');
|
|
}
|
|
|
|
async executePush(e) {
|
|
e.preventDefault();
|
|
|
|
const modal = $('#git-push-modal');
|
|
const isServerMode = modal?.dataset.serverMode === 'true';
|
|
|
|
// Modal schließen
|
|
modal?.classList.add('hidden');
|
|
modal?.classList.remove('visible');
|
|
$('#modal-overlay')?.classList.add('hidden');
|
|
store.closeModal();
|
|
|
|
if (isServerMode) {
|
|
// Server-Modus: Push für Server-Dateien
|
|
delete modal.dataset.serverMode;
|
|
const forcePush = $('#push-force')?.checked || false;
|
|
await this.executeServerPush(forcePush);
|
|
} else {
|
|
// Projekt-Modus: Push für Projekt-Repository
|
|
const projectId = store.get('currentProjectId');
|
|
if (!projectId) return;
|
|
|
|
this.setOperationLoading('push', true);
|
|
|
|
try {
|
|
const targetBranch = $('#push-target-branch')?.value || null;
|
|
const forcePush = $('#push-force')?.checked || false;
|
|
const localBranch = $('#branch-select')?.value || this.gitStatus?.branch || 'master';
|
|
|
|
let result;
|
|
|
|
// Wenn ein anderer Target-Branch ausgewählt wurde oder Force-Push, verwende init-push
|
|
if ((targetBranch && targetBranch !== localBranch) || forcePush) {
|
|
const forceText = forcePush ? ' (Force)' : '';
|
|
const branchText = targetBranch && targetBranch !== localBranch
|
|
? `Push von "${localBranch}" nach "${targetBranch}"${forceText}...`
|
|
: `Push nach "${localBranch}"${forceText}...`;
|
|
this.showToast(branchText, 'info');
|
|
result = await api.gitInitPush(projectId, targetBranch, forcePush);
|
|
} else {
|
|
// Normaler Push (gleicher Branch-Name)
|
|
result = await api.gitPush(projectId, localBranch);
|
|
|
|
// Falls Push wegen fehlendem Upstream/Remote fehlschlägt, versuche init-push
|
|
if (!result.success && result.error &&
|
|
(result.error.includes('No configured push destination') ||
|
|
result.error.includes('no upstream') ||
|
|
result.error.includes('Kein Remote'))) {
|
|
this.showToast('Kein Upstream konfiguriert, führe initialen Push durch...', 'info');
|
|
result = await api.gitInitPush(projectId, targetBranch, false);
|
|
}
|
|
}
|
|
|
|
if (result.success) {
|
|
const pushedBranch = result.branch || targetBranch || localBranch;
|
|
this.showToast(`Push erfolgreich nach Branch "${pushedBranch}"`, 'success');
|
|
await this.loadGitData();
|
|
} else {
|
|
this.showToast(result.error || 'Push fehlgeschlagen', 'error');
|
|
}
|
|
} catch (error) {
|
|
this.showToast(error.message || 'Push fehlgeschlagen', 'error');
|
|
} finally {
|
|
this.setOperationLoading('push', false);
|
|
}
|
|
}
|
|
}
|
|
|
|
openRenameBranchModal() {
|
|
const modal = $('#git-rename-branch-modal');
|
|
if (!modal) return;
|
|
|
|
const currentBranch = $('#branch-select')?.value || this.gitStatus?.branch || 'master';
|
|
$('#rename-current-branch').textContent = currentBranch;
|
|
$('#rename-new-branch').value = '';
|
|
|
|
modal.classList.remove('hidden');
|
|
modal.classList.add('visible');
|
|
$('#modal-overlay')?.classList.remove('hidden');
|
|
|
|
store.openModal('git-rename-branch-modal');
|
|
|
|
// Focus auf das Eingabefeld
|
|
setTimeout(() => $('#rename-new-branch')?.focus(), 100);
|
|
}
|
|
|
|
async executeRenameBranch(e) {
|
|
e.preventDefault();
|
|
|
|
const projectId = store.get('currentProjectId');
|
|
if (!projectId) return;
|
|
|
|
const oldName = $('#rename-current-branch')?.textContent;
|
|
const newName = $('#rename-new-branch')?.value.trim();
|
|
|
|
if (!newName) {
|
|
this.showToast('Bitte geben Sie einen neuen Branch-Namen ein', 'error');
|
|
return;
|
|
}
|
|
|
|
if (oldName === newName) {
|
|
this.showToast('Der neue Name ist identisch mit dem aktuellen', 'error');
|
|
return;
|
|
}
|
|
|
|
// Modal schließen
|
|
const modal = $('#git-rename-branch-modal');
|
|
modal?.classList.add('hidden');
|
|
modal?.classList.remove('visible');
|
|
$('#modal-overlay')?.classList.add('hidden');
|
|
store.closeModal();
|
|
|
|
try {
|
|
const result = await api.gitRenameBranch(projectId, oldName, newName);
|
|
|
|
if (result.success) {
|
|
this.showToast(`Branch umbenannt: "${oldName}" → "${newName}"`, 'success');
|
|
await this.loadGitData();
|
|
} else {
|
|
this.showToast(result.error || 'Umbenennen fehlgeschlagen', 'error');
|
|
}
|
|
} catch (error) {
|
|
this.showToast(error.message || 'Umbenennen fehlgeschlagen', 'error');
|
|
}
|
|
}
|
|
|
|
openCommitModal() {
|
|
const modal = $('#git-commit-modal');
|
|
if (!modal) return;
|
|
|
|
$('#commit-message').value = '';
|
|
$('#commit-stage-all').checked = true;
|
|
|
|
modal.classList.remove('hidden');
|
|
modal.classList.add('visible');
|
|
$('#modal-overlay')?.classList.remove('hidden');
|
|
|
|
store.openModal('git-commit-modal');
|
|
}
|
|
|
|
async handleCommit(e) {
|
|
e.preventDefault();
|
|
|
|
const modal = $('#git-commit-modal');
|
|
const isServerMode = modal?.dataset.serverMode === 'true';
|
|
|
|
const message = $('#commit-message')?.value.trim();
|
|
const stageAll = $('#commit-stage-all')?.checked ?? true;
|
|
|
|
if (!message) {
|
|
this.showToast('Bitte geben Sie eine Commit-Nachricht ein', 'error');
|
|
return;
|
|
}
|
|
|
|
if (isServerMode) {
|
|
// Server-Modus: Commit für Server-Dateien
|
|
const success = await this.executeServerCommit(message, stageAll);
|
|
if (success) {
|
|
this.closeModal('git-commit-modal');
|
|
delete modal.dataset.serverMode;
|
|
}
|
|
} else {
|
|
// Projekt-Modus: Commit für Projekt-Repository
|
|
const projectId = store.get('currentProjectId');
|
|
if (!projectId) return;
|
|
|
|
try {
|
|
const result = await api.gitCommit(projectId, message, stageAll);
|
|
|
|
if (result.success) {
|
|
this.showToast('Commit erstellt', 'success');
|
|
this.closeModal('git-commit-modal');
|
|
await this.loadGitData();
|
|
} else {
|
|
this.showToast(result.error || 'Commit fehlgeschlagen', 'error');
|
|
}
|
|
} catch (error) {
|
|
this.showToast(error.message || 'Commit fehlgeschlagen', 'error');
|
|
}
|
|
}
|
|
}
|
|
|
|
async handleBranchChange(e) {
|
|
const projectId = store.get('currentProjectId');
|
|
const branch = e.target.value;
|
|
|
|
if (!projectId || !branch) return;
|
|
|
|
try {
|
|
const result = await api.gitCheckout(projectId, branch);
|
|
|
|
if (result.success) {
|
|
this.showToast(`Gewechselt zu ${branch}`, 'success');
|
|
await this.loadGitData();
|
|
} else {
|
|
this.showToast(result.error || 'Branch-Wechsel fehlgeschlagen', 'error');
|
|
// Zurück zum vorherigen Branch
|
|
await this.loadGitData();
|
|
}
|
|
} catch (error) {
|
|
this.showToast(error.message || 'Branch-Wechsel fehlgeschlagen', 'error');
|
|
await this.loadGitData();
|
|
}
|
|
}
|
|
|
|
openCreateRepoModal() {
|
|
const modal = $('#create-repo-modal');
|
|
if (!modal) return;
|
|
|
|
$('#new-repo-name').value = '';
|
|
$('#new-repo-description').value = '';
|
|
$('#new-repo-private').checked = true;
|
|
|
|
modal.classList.remove('hidden');
|
|
modal.classList.add('visible');
|
|
$('#modal-overlay')?.classList.remove('hidden');
|
|
|
|
store.openModal('create-repo-modal');
|
|
}
|
|
|
|
async handleCreateRepo(e) {
|
|
e.preventDefault();
|
|
|
|
const name = $('#new-repo-name')?.value.trim();
|
|
const description = $('#new-repo-description')?.value.trim();
|
|
const isPrivate = $('#new-repo-private')?.checked ?? true;
|
|
|
|
if (!name) {
|
|
this.showToast('Bitte geben Sie einen Repository-Namen ein', 'error');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const result = await api.createGiteaRepository({
|
|
name,
|
|
description,
|
|
private: isPrivate
|
|
});
|
|
|
|
if (result.success) {
|
|
this.showToast('Repository erstellt', 'success');
|
|
this.closeModal('create-repo-modal');
|
|
await this.loadGiteaRepos();
|
|
|
|
// Neues Repository im Dropdown auswählen
|
|
const select = $('#gitea-repo-select');
|
|
if (select && result.repository) {
|
|
const option = document.createElement('option');
|
|
option.value = JSON.stringify({
|
|
url: result.repository.cloneUrl,
|
|
owner: result.repository.owner,
|
|
name: result.repository.name,
|
|
fullName: result.repository.fullName,
|
|
htmlUrl: result.repository.htmlUrl,
|
|
defaultBranch: result.repository.defaultBranch
|
|
});
|
|
option.textContent = result.repository.fullName;
|
|
select.appendChild(option);
|
|
select.value = option.value;
|
|
}
|
|
} else {
|
|
this.showToast(result.error || 'Repository-Erstellung fehlgeschlagen', 'error');
|
|
}
|
|
} catch (error) {
|
|
this.showToast(error.message || 'Repository-Erstellung fehlgeschlagen', 'error');
|
|
}
|
|
}
|
|
|
|
// Rendering
|
|
renderConfigurationView() {
|
|
this.hideAllSections();
|
|
this.configSection?.classList.remove('hidden');
|
|
|
|
// Formular zurücksetzen
|
|
$('#gitea-repo-select').value = '';
|
|
$('#local-path-input').value = '';
|
|
$('#default-branch-input').value = 'main';
|
|
$('#path-validation-result').textContent = '';
|
|
}
|
|
|
|
renderConfiguredView() {
|
|
this.hideAllSections();
|
|
this.mainSection?.classList.remove('hidden');
|
|
|
|
// Repository-Info
|
|
const repoNameEl = $('#gitea-repo-name span');
|
|
const repoUrlEl = $('#gitea-repo-url');
|
|
const localPathEl = $('#gitea-local-path-display');
|
|
|
|
if (this.application) {
|
|
if (this.application.gitea_repo_owner && this.application.gitea_repo_name) {
|
|
repoNameEl.textContent = `${this.application.gitea_repo_owner}/${this.application.gitea_repo_name}`;
|
|
} else {
|
|
repoNameEl.textContent = 'Lokales Repository';
|
|
}
|
|
|
|
if (this.application.gitea_repo_url) {
|
|
repoUrlEl.href = this.application.gitea_repo_url;
|
|
repoUrlEl.textContent = this.application.gitea_repo_url;
|
|
repoUrlEl.classList.remove('hidden');
|
|
} else {
|
|
repoUrlEl.classList.add('hidden');
|
|
}
|
|
|
|
localPathEl.textContent = this.application.local_path || '-';
|
|
}
|
|
}
|
|
|
|
renderStatus() {
|
|
const statusBadge = $('#git-status-indicator');
|
|
const changesCount = $('#git-changes-count');
|
|
|
|
if (!this.gitStatus) {
|
|
statusBadge.textContent = 'Fehler';
|
|
statusBadge.className = 'status-badge error';
|
|
changesCount.textContent = '-';
|
|
return;
|
|
}
|
|
|
|
// Status Badge
|
|
if (!this.gitStatus.success) {
|
|
statusBadge.textContent = 'Fehler';
|
|
statusBadge.className = 'status-badge error';
|
|
} else if (this.gitStatus.isClean) {
|
|
statusBadge.textContent = 'Sauber';
|
|
statusBadge.className = 'status-badge clean';
|
|
} else if (this.gitStatus.hasChanges) {
|
|
statusBadge.textContent = 'Geändert';
|
|
statusBadge.className = 'status-badge dirty';
|
|
} else {
|
|
statusBadge.textContent = 'OK';
|
|
statusBadge.className = 'status-badge clean';
|
|
}
|
|
|
|
// Änderungen
|
|
const changes = this.gitStatus.changes || [];
|
|
changesCount.textContent = changes.length;
|
|
}
|
|
|
|
renderBranches() {
|
|
const select = $('#branch-select');
|
|
if (!select) return;
|
|
|
|
const currentBranch = this.gitStatus?.branch || 'main';
|
|
|
|
select.innerHTML = '';
|
|
|
|
// Lokale Branches zuerst
|
|
const localBranches = this.branches.filter(b => !b.isRemote);
|
|
localBranches.forEach(branch => {
|
|
const option = document.createElement('option');
|
|
option.value = branch.name;
|
|
option.textContent = branch.name;
|
|
if (branch.name === currentBranch) {
|
|
option.selected = true;
|
|
}
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
|
|
renderCommits() {
|
|
const listEl = $('#git-commits-list');
|
|
const clearBtn = $('#btn-clear-commits');
|
|
if (!listEl) return;
|
|
|
|
// Filtern: Ausgeblendete Commits nicht anzeigen
|
|
const visibleCommits = this.commits.filter(commit => {
|
|
const hash = commit.hash || commit.sha || commit.shortHash;
|
|
return !this.hiddenCommits.has(hash);
|
|
});
|
|
|
|
// Button-Text anpassen
|
|
if (clearBtn) {
|
|
clearBtn.style.display = visibleCommits.length > 0 ? '' : 'none';
|
|
}
|
|
|
|
if (visibleCommits.length === 0) {
|
|
const message = this.commits.length > 0
|
|
? 'Alle Commits ausgeblendet'
|
|
: 'Keine Commits gefunden';
|
|
listEl.innerHTML = `<div class="gitea-empty-state" style="padding: var(--spacing-4);"><p>${message}</p></div>`;
|
|
return;
|
|
}
|
|
|
|
listEl.innerHTML = visibleCommits.map(commit => {
|
|
const hash = commit.hash || commit.sha || '';
|
|
const shortHash = commit.shortHash || hash.substring(0, 7);
|
|
return `
|
|
<div class="commit-item" data-hash="${escapeHtml(hash)}">
|
|
<span class="commit-hash">${escapeHtml(shortHash)}</span>
|
|
<div class="commit-info">
|
|
<div class="commit-message">${escapeHtml(commit.message?.split('\n')[0] || '')}</div>
|
|
<div class="commit-meta">
|
|
<span class="author">${escapeHtml(commit.author)}</span> · ${this.formatDate(commit.date)}
|
|
</div>
|
|
</div>
|
|
<button class="commit-delete" title="Ausblenden" data-hash="${escapeHtml(hash)}">
|
|
<svg viewBox="0 0 24 24" width="16" height="16"><path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
|
</button>
|
|
</div>
|
|
`}).join('');
|
|
}
|
|
|
|
handleCommitListClick(e) {
|
|
const deleteBtn = e.target.closest('.commit-delete');
|
|
if (deleteBtn) {
|
|
const hash = deleteBtn.dataset.hash;
|
|
if (hash) {
|
|
this.hideCommit(hash);
|
|
}
|
|
}
|
|
}
|
|
|
|
hideCommit(hash) {
|
|
this.hiddenCommits.add(hash);
|
|
this.renderCommits();
|
|
this.showToast('Commit ausgeblendet', 'info');
|
|
}
|
|
|
|
clearAllCommits() {
|
|
// Alle sichtbaren Commits ausblenden
|
|
this.commits.forEach(commit => {
|
|
const hash = commit.hash || commit.sha || commit.shortHash;
|
|
if (hash) {
|
|
this.hiddenCommits.add(hash);
|
|
}
|
|
});
|
|
this.renderCommits();
|
|
this.showToast('Alle Commits ausgeblendet', 'info');
|
|
}
|
|
|
|
renderChanges() {
|
|
const changesSection = $('#gitea-changes-section');
|
|
const listEl = $('#git-changes-list');
|
|
|
|
if (!changesSection || !listEl) return;
|
|
|
|
const changes = this.gitStatus?.changes || [];
|
|
|
|
if (changes.length === 0) {
|
|
changesSection.classList.add('hidden');
|
|
return;
|
|
}
|
|
|
|
changesSection.classList.remove('hidden');
|
|
|
|
listEl.innerHTML = changes.map(change => {
|
|
const statusClass = this.getChangeStatusClass(change.status);
|
|
const statusLabel = this.getChangeStatusLabel(change.status);
|
|
|
|
return `
|
|
<div class="change-item">
|
|
<span class="change-status ${statusClass}" title="${statusLabel}">${change.status}</span>
|
|
<span class="change-file">${escapeHtml(change.file)}</span>
|
|
</div>
|
|
`;
|
|
}).join('');
|
|
}
|
|
|
|
getChangeStatusClass(status) {
|
|
const first = status.charAt(0);
|
|
const second = status.charAt(1);
|
|
|
|
if (first === 'M' || second === 'M') return 'modified';
|
|
if (first === 'A' || second === 'A') return 'added';
|
|
if (first === 'D' || second === 'D') return 'deleted';
|
|
if (first === 'R' || second === 'R') return 'renamed';
|
|
if (first === '?' || second === '?') return 'untracked';
|
|
return '';
|
|
}
|
|
|
|
getChangeStatusLabel(status) {
|
|
const first = status.charAt(0);
|
|
const second = status.charAt(1);
|
|
|
|
if (first === 'M' || second === 'M') return 'Geändert';
|
|
if (first === 'A' || second === 'A') return 'Hinzugefügt';
|
|
if (first === 'D' || second === 'D') return 'Gelöscht';
|
|
if (first === 'R' || second === 'R') return 'Umbenannt';
|
|
if (first === '?' || second === '?') return 'Nicht verfolgt';
|
|
return status;
|
|
}
|
|
|
|
formatDate(dateStr) {
|
|
if (!dateStr) return '';
|
|
|
|
const date = new Date(dateStr);
|
|
const now = new Date();
|
|
const diffMs = now - date;
|
|
const diffMins = Math.floor(diffMs / 60000);
|
|
const diffHours = Math.floor(diffMs / 3600000);
|
|
const diffDays = Math.floor(diffMs / 86400000);
|
|
|
|
if (diffMins < 1) return 'gerade eben';
|
|
if (diffMins < 60) return `vor ${diffMins} Minute${diffMins !== 1 ? 'n' : ''}`;
|
|
if (diffHours < 24) return `vor ${diffHours} Stunde${diffHours !== 1 ? 'n' : ''}`;
|
|
if (diffDays < 7) return `vor ${diffDays} Tag${diffDays !== 1 ? 'en' : ''}`;
|
|
|
|
// Lokale Formatierung
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
const year = date.getFullYear();
|
|
return `${day}.${month}.${year}`;
|
|
}
|
|
|
|
// UI Helpers
|
|
hideAllSections() {
|
|
this.noProjectSection?.classList.add('hidden');
|
|
this.configSection?.classList.add('hidden');
|
|
this.mainSection?.classList.add('hidden');
|
|
}
|
|
|
|
showNoProjectMessage() {
|
|
this.hideAllSections();
|
|
this.noProjectSection?.classList.remove('hidden');
|
|
}
|
|
|
|
showLoading() {
|
|
this.isLoading = true;
|
|
}
|
|
|
|
showError(message) {
|
|
this.showToast(message, 'error');
|
|
}
|
|
|
|
showToast(message, type = 'info') {
|
|
window.dispatchEvent(new CustomEvent('toast:show', {
|
|
detail: { message, type }
|
|
}));
|
|
}
|
|
|
|
closeModal(modalId) {
|
|
const modal = $(`#${modalId}`);
|
|
if (modal) {
|
|
modal.classList.add('hidden');
|
|
modal.classList.remove('visible');
|
|
}
|
|
$('#modal-overlay')?.classList.add('hidden');
|
|
store.closeModal(modalId);
|
|
}
|
|
|
|
setOperationLoading(operation, loading) {
|
|
const buttonId = `btn-git-${operation}`;
|
|
const button = $(`#${buttonId}`);
|
|
if (button) {
|
|
button.disabled = loading;
|
|
if (loading) {
|
|
button.classList.add('loading');
|
|
} else {
|
|
button.classList.remove('loading');
|
|
}
|
|
}
|
|
}
|
|
|
|
// View Control
|
|
show() {
|
|
this.giteaView?.classList.remove('hidden');
|
|
this.giteaView?.classList.add('active');
|
|
this.updateModeView();
|
|
this.startAutoRefresh();
|
|
}
|
|
|
|
hide() {
|
|
this.giteaView?.classList.add('hidden');
|
|
this.giteaView?.classList.remove('active');
|
|
this.stopAutoRefresh();
|
|
}
|
|
|
|
startAutoRefresh() {
|
|
this.stopAutoRefresh();
|
|
// Status alle 30 Sekunden aktualisieren
|
|
this.refreshInterval = setInterval(() => {
|
|
if (this.isLoading) return;
|
|
|
|
if (this.currentMode === 'server') {
|
|
// Server-Modus: Server-Daten aktualisieren
|
|
this.loadServerGitData();
|
|
} else if (this.application?.configured) {
|
|
// Projekt-Modus: Projekt-Daten aktualisieren
|
|
this.loadGitData();
|
|
}
|
|
}, 30000);
|
|
}
|
|
|
|
stopAutoRefresh() {
|
|
if (this.refreshInterval) {
|
|
clearInterval(this.refreshInterval);
|
|
this.refreshInterval = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
const giteaManager = new GiteaManager();
|
|
export { giteaManager };
|
|
export default giteaManager;
|