Update Gitea-Sektion:
Branch-Auswahl
Dieser Commit ist enthalten in:
@ -225,6 +225,12 @@
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.branch-select-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-2);
|
||||
}
|
||||
|
||||
.branch-select {
|
||||
padding: var(--spacing-2) var(--spacing-3);
|
||||
border-radius: var(--radius-md);
|
||||
@ -233,6 +239,7 @@
|
||||
color: var(--text-primary);
|
||||
font-size: var(--text-sm);
|
||||
cursor: pointer;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.branch-select:focus {
|
||||
@ -240,6 +247,24 @@
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.btn-small {
|
||||
padding: var(--spacing-1) var(--spacing-2);
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--spacing-1);
|
||||
min-width: 28px;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.btn-icon svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@ -259,11 +284,6 @@
|
||||
color: var(--warning);
|
||||
}
|
||||
|
||||
.status-badge.ahead {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
color: var(--info);
|
||||
}
|
||||
|
||||
.status-badge.error {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
color: var(--error);
|
||||
@ -275,12 +295,6 @@
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
#git-ahead-behind {
|
||||
font-size: var(--text-sm);
|
||||
color: var(--text-primary);
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
OPERATIONS PANEL
|
||||
============================================================================= */
|
||||
@ -511,6 +525,20 @@
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.form-hint.warning {
|
||||
color: var(--warning);
|
||||
}
|
||||
|
||||
.form-static-value {
|
||||
padding: var(--spacing-2) var(--spacing-3);
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-md);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-sm);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@ -606,9 +606,14 @@
|
||||
<div class="status-grid">
|
||||
<div class="status-item">
|
||||
<span class="status-label">Branch</span>
|
||||
<select id="branch-select" class="branch-select">
|
||||
<!-- Branches dynamisch -->
|
||||
</select>
|
||||
<div class="branch-select-group">
|
||||
<select id="branch-select" class="branch-select">
|
||||
<!-- Branches dynamisch -->
|
||||
</select>
|
||||
<button id="btn-rename-branch" class="btn btn-small btn-icon" title="Branch umbenennen">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16"><path d="M17 3a2.85 2.85 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z" stroke="currentColor" stroke-width="2" fill="none"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-label">Status</span>
|
||||
@ -618,10 +623,6 @@
|
||||
<span class="status-label">Änderungen</span>
|
||||
<span id="git-changes-count" class="changes-count">0</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-label">Ahead / Behind</span>
|
||||
<span id="git-ahead-behind">- / -</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1163,6 +1164,70 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Git Branch Rename Modal -->
|
||||
<div id="git-rename-branch-modal" class="modal modal-small hidden">
|
||||
<div class="modal-header">
|
||||
<h2>Branch umbenennen</h2>
|
||||
<button class="modal-close" data-close-modal>×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="git-rename-branch-form">
|
||||
<div class="form-group">
|
||||
<label>Aktueller Branch</label>
|
||||
<div id="rename-current-branch" class="form-static-value">master</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="rename-new-branch">Neuer Name *</label>
|
||||
<input type="text" id="rename-new-branch" required
|
||||
placeholder="z.B. main" pattern="[a-zA-Z0-9_\-]+"
|
||||
title="Nur Buchstaben, Zahlen, Unterstriche und Bindestriche">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-close-modal>Abbrechen</button>
|
||||
<button type="submit" form="git-rename-branch-form" class="btn btn-primary">Umbenennen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Git Push Modal -->
|
||||
<div id="git-push-modal" class="modal modal-small hidden">
|
||||
<div class="modal-header">
|
||||
<h2>Push zu Gitea</h2>
|
||||
<button class="modal-close" data-close-modal>×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="git-push-form">
|
||||
<div class="form-group">
|
||||
<label>Lokaler Branch</label>
|
||||
<div id="push-local-branch" class="form-static-value">wird ermittelt...</div>
|
||||
<span class="form-hint">Der aktuelle lokale Branch wird automatisch erkannt</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="push-target-branch">Ziel-Branch auf Remote</label>
|
||||
<select id="push-target-branch">
|
||||
<option value="">Gleicher Name wie lokal</option>
|
||||
<option value="main">main</option>
|
||||
<option value="master">master</option>
|
||||
<option value="develop">develop</option>
|
||||
</select>
|
||||
<span class="form-hint">Wählen Sie den Branch-Namen auf Gitea</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="push-force">
|
||||
Force Push (Remote überschreiben)
|
||||
</label>
|
||||
<span class="form-hint warning">Achtung: Überschreibt alle Änderungen auf dem Remote-Branch!</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-close-modal>Abbrechen</button>
|
||||
<button type="submit" form="git-push-form" class="btn btn-primary">Push starten</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Create Repository Modal -->
|
||||
<div id="create-repo-modal" class="modal modal-small hidden">
|
||||
<div class="modal-header">
|
||||
|
||||
@ -820,8 +820,12 @@ class ApiClient {
|
||||
return this.post(`/git/set-remote/${projectId}`, { repoUrl });
|
||||
}
|
||||
|
||||
async gitInitPush(projectId, branch = 'main') {
|
||||
return this.post(`/git/init-push/${projectId}`, { branch });
|
||||
async gitInitPush(projectId, targetBranch = null, force = false) {
|
||||
return this.post(`/git/init-push/${projectId}`, { targetBranch, force });
|
||||
}
|
||||
|
||||
async gitRenameBranch(projectId, oldName, newName) {
|
||||
return this.post(`/git/rename-branch/${projectId}`, { oldName, newName });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -72,6 +72,13 @@ class GiteaManager {
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
subscribeToStore() {
|
||||
@ -406,27 +413,76 @@ class GiteaManager {
|
||||
}
|
||||
}
|
||||
|
||||
async handlePush() {
|
||||
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 projectId = store.get('currentProjectId');
|
||||
if (!projectId) return;
|
||||
|
||||
// Modal schließen
|
||||
const modal = $('#git-push-modal');
|
||||
modal?.classList.add('hidden');
|
||||
modal?.classList.remove('visible');
|
||||
$('#modal-overlay')?.classList.add('hidden');
|
||||
store.closeModal();
|
||||
|
||||
this.setOperationLoading('push', true);
|
||||
|
||||
try {
|
||||
const branch = $('#branch-select')?.value || 'main';
|
||||
let result = await api.gitPush(projectId, branch);
|
||||
const targetBranch = $('#push-target-branch')?.value || null;
|
||||
const forcePush = $('#push-force')?.checked || false;
|
||||
const localBranch = $('#branch-select')?.value || this.gitStatus?.branch || 'master';
|
||||
|
||||
// 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, branch);
|
||||
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) {
|
||||
this.showToast('Push erfolgreich', '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');
|
||||
@ -438,6 +494,64 @@ class GiteaManager {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@ -610,13 +724,11 @@ class GiteaManager {
|
||||
renderStatus() {
|
||||
const statusBadge = $('#git-status-indicator');
|
||||
const changesCount = $('#git-changes-count');
|
||||
const aheadBehind = $('#git-ahead-behind');
|
||||
|
||||
if (!this.gitStatus) {
|
||||
statusBadge.textContent = 'Fehler';
|
||||
statusBadge.className = 'status-badge error';
|
||||
changesCount.textContent = '-';
|
||||
aheadBehind.textContent = '- / -';
|
||||
return;
|
||||
}
|
||||
|
||||
@ -630,9 +742,6 @@ class GiteaManager {
|
||||
} else if (this.gitStatus.hasChanges) {
|
||||
statusBadge.textContent = 'Geändert';
|
||||
statusBadge.className = 'status-badge dirty';
|
||||
} else if (this.gitStatus.ahead > 0) {
|
||||
statusBadge.textContent = 'Voraus';
|
||||
statusBadge.className = 'status-badge ahead';
|
||||
} else {
|
||||
statusBadge.textContent = 'OK';
|
||||
statusBadge.className = 'status-badge clean';
|
||||
@ -641,11 +750,6 @@ class GiteaManager {
|
||||
// Änderungen
|
||||
const changes = this.gitStatus.changes || [];
|
||||
changesCount.textContent = changes.length;
|
||||
|
||||
// Ahead/Behind
|
||||
const ahead = this.gitStatus.ahead || 0;
|
||||
const behind = this.gitStatus.behind || 0;
|
||||
aheadBehind.textContent = `${ahead} / ${behind}`;
|
||||
}
|
||||
|
||||
renderBranches() {
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
* Offline support and caching
|
||||
*/
|
||||
|
||||
const CACHE_VERSION = '118';
|
||||
const CACHE_VERSION = '123';
|
||||
const CACHE_NAME = 'taskmate-v' + CACHE_VERSION;
|
||||
const STATIC_CACHE_NAME = 'taskmate-static-v' + CACHE_VERSION;
|
||||
const DYNAMIC_CACHE_NAME = 'taskmate-dynamic-v' + CACHE_VERSION;
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren