diff --git a/src/views/ProjectsView.vue b/src/views/ProjectsView.vue index 42e6a6a..fe46522 100644 --- a/src/views/ProjectsView.vue +++ b/src/views/ProjectsView.vue @@ -19,6 +19,13 @@ interface GitLabProjectRef { name: string } +interface GiteaProjectRef { + projectId: number + path: string + url: string + name: string +} + interface Project { _id: string name: string @@ -26,9 +33,7 @@ interface Project { color: string rules?: string gitlabProjects?: GitLabProjectRef[] - gitlabProjectId?: number - gitlabUrl?: string - gitlabPath?: string + giteaProjects?: GiteaProjectRef[] } interface GitLabProject { @@ -40,11 +45,22 @@ interface GitLabProject { webUrl: string } +interface GiteaProject { + id: number + name: string + fullName: string + path: string + description: string | null + webUrl: string + owner: string +} + const authStore = useAuthStore() const toast = useToast() const projects = ref([]) const tasks = ref([]) const gitlabProjects = ref([]) +const giteaProjects = ref([]) const loading = ref(true) const showEditModal = ref(false) @@ -56,7 +72,8 @@ const newProject = ref({ description: '', color: '#6366f1', rules: '', - gitlabProjects: [] as GitLabProjectRef[] + gitlabProjects: [] as GitLabProjectRef[], + giteaProjects: [] as GiteaProjectRef[] }) async function loadData() { @@ -83,14 +100,21 @@ async function loadGitlabProjects() { } } +async function loadGiteaProjects() { + try { + giteaProjects.value = await api.get('/gitea/projects') + } catch (err) { + console.error('Failed to load Gitea projects:', err) + } +} + +// GitLab selection const selectedGitlabIds = ref([]) function initGitlabSelection() { if (!editingProject.value) return if (editingProject.value.gitlabProjects?.length) { selectedGitlabIds.value = editingProject.value.gitlabProjects.map(p => p.projectId) - } else if (editingProject.value.gitlabProjectId) { - selectedGitlabIds.value = [editingProject.value.gitlabProjectId] } else { selectedGitlabIds.value = [] } @@ -128,6 +152,50 @@ function isGitlabSelected(id: number): boolean { return selectedGitlabIds.value.includes(id) } +// Gitea selection +const selectedGiteaIds = ref([]) + +function initGiteaSelection() { + if (!editingProject.value) return + if (editingProject.value.giteaProjects?.length) { + selectedGiteaIds.value = editingProject.value.giteaProjects.map(p => p.projectId) + } else { + selectedGiteaIds.value = [] + } +} + +function toggleGiteaProject(giteaProject: GiteaProject) { + const idx = selectedGiteaIds.value.indexOf(giteaProject.id) + if (idx >= 0) { + selectedGiteaIds.value.splice(idx, 1) + } else { + selectedGiteaIds.value.push(giteaProject.id) + } + updateGiteaProjects() +} + +function updateGiteaProjects() { + const gtProjects = selectedGiteaIds.value.map(id => { + const gp = giteaProjects.value.find(p => p.id === id) + return gp ? { + projectId: gp.id, + path: gp.fullName, + url: gp.webUrl, + name: gp.name + } : null + }).filter(Boolean) as GiteaProjectRef[] + + if (editingProject.value) { + editingProject.value.giteaProjects = gtProjects + } else { + newProject.value.giteaProjects = gtProjects + } +} + +function isGiteaSelected(id: number): boolean { + return selectedGiteaIds.value.includes(id) +} + function getProjectTasks(projectId: string) { return tasks.value.filter(t => t.project === projectId) } @@ -148,15 +216,18 @@ function openCreateModal() { description: '', color: '#6366f1', rules: '', - gitlabProjects: [] + gitlabProjects: [], + giteaProjects: [] } selectedGitlabIds.value = [] + selectedGiteaIds.value = [] showCreateModal.value = true } function editProject(project: Project) { editingProject.value = { ...project } initGitlabSelection() + initGiteaSelection() showEditModal.value = true } @@ -172,7 +243,8 @@ async function createProject() { description: newProject.value.description, color: newProject.value.color, rules: newProject.value.rules, - gitlabProjects: newProject.value.gitlabProjects + gitlabProjects: newProject.value.gitlabProjects, + giteaProjects: newProject.value.giteaProjects }) toast.add({ severity: 'success', summary: 'Erstellt', detail: 'Projekt wurde angelegt', life: 3000 }) showCreateModal.value = false @@ -192,9 +264,7 @@ async function saveProject() { color: editingProject.value.color, rules: editingProject.value.rules, gitlabProjects: editingProject.value.gitlabProjects, - gitlabProjectId: editingProject.value.gitlabProjects?.[0]?.projectId, - gitlabUrl: editingProject.value.gitlabProjects?.[0]?.url, - gitlabPath: editingProject.value.gitlabProjects?.[0]?.path + giteaProjects: editingProject.value.giteaProjects }) toast.add({ severity: 'success', summary: 'Gespeichert', detail: 'Projekt aktualisiert', life: 3000 }) showEditModal.value = false @@ -219,6 +289,7 @@ async function deleteProject(id: string, name: string) { onMounted(() => { loadData() loadGitlabProjects() + loadGiteaProjects() }) @@ -272,23 +343,26 @@ onMounted(() => { -
-

GitLab

- - +
+ + + +
+

Gitea

+
@@ -362,20 +436,41 @@ onMounted(() => {
-
-

GitLab Projekte

+ +
+

Gitea Projekte

- -
+ +
+
+ + {{ gp.fullName }} +
+
+
+
+ + +
+

GitLab Projekte

+
+ +
- {{ gp.fullName }} + {{ gp.fullName }}
@@ -420,23 +515,50 @@ onMounted(() => {
-
-

GitLab Projekte

+ +
+

Gitea Projekte

- -
+ +
+
+ + {{ gp.fullName }} +
+
+
+
+ {{ editingProject.giteaProjects.length }} Gitea-Repo(s) ausgewählt +
+
+ + +
+

GitLab Projekte

+
+ +
- {{ gp.fullName }} + {{ gp.fullName }}
+
+ {{ editingProject.gitlabProjects.length }} GitLab-Repo(s) ausgewählt +
@@ -539,13 +661,13 @@ onMounted(() => { .stat.progress .stat-value { color: #fbbf24; } .stat.todo .stat-value { color: #60a5fa; } -.project-rules, .project-gitlab { +.project-rules, .project-git { padding: 1rem 1.25rem; background: rgba(255,255,255,0.03); border-bottom: 1px solid rgba(255,255,255,0.05); } -.project-rules h4, .project-gitlab h4 { +.project-rules h4, .project-git h4 { margin: 0 0 0.5rem; font-size: 0.875rem; color: rgba(255,255,255,0.7); @@ -554,9 +676,24 @@ onMounted(() => { gap: 0.5rem; } -.project-gitlab h4 { color: #fb923c; } +.project-git.gitlab h4 { color: #fb923c; } +.project-git.gitea h4 { color: #22c55e; } .project-rules p { margin: 0; font-size: 0.875rem; color: rgba(255,255,255,0.6); } +.git-project-item { padding: 0.25rem 0; } + +.git-link { + display: inline-flex; + align-items: center; + gap: 0.375rem; + text-decoration: none; + font-size: 0.875rem; + font-family: monospace; +} +.project-git.gitlab .git-link { color: #fb923c; } +.project-git.gitea .git-link { color: #22c55e; } +.git-link:hover { text-decoration: underline; } + .project-tasks { padding: 1rem 1.25rem; } .project-tasks h4 { margin: 0 0 0.75rem; font-size: 0.875rem; color: rgba(255,255,255,0.7); } @@ -594,11 +731,6 @@ onMounted(() => { .empty-state i { font-size: 3rem; margin-bottom: 1rem; opacity: 0.5; } .empty-state p { margin-bottom: 1.5rem; } -.gitlab-link { display: inline-flex; align-items: center; gap: 0.375rem; color: #fb923c; text-decoration: none; font-size: 0.875rem; font-family: monospace; } -.gitlab-link:hover { text-decoration: underline; } -.gitlab-id { margin-left: 0.75rem; color: rgba(255,255,255,0.4); font-size: 0.75rem; } -.gitlab-project-item { display: flex; align-items: center; padding: 0.25rem 0; } - .modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; @@ -616,7 +748,7 @@ onMounted(() => { border-radius: 16px; padding: 1.25rem; width: 100%; - max-width: 500px; + max-width: 550px; max-height: 85vh; display: flex; flex-direction: column; @@ -657,15 +789,18 @@ onMounted(() => { .cancel-btn { padding: 0.75rem 1rem; background: transparent; border: 1px solid rgba(255,255,255,0.2); border-radius: 8px; color: rgba(255,255,255,0.7); cursor: pointer; } .submit-btn { padding: 0.75rem 1.5rem; background: linear-gradient(135deg, #6366f1, #8b5cf6); border: none; border-radius: 8px; color: #fff; font-weight: 600; cursor: pointer; } -.gitlab-section { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid rgba(255,255,255,0.1); } -.gitlab-section h4 { margin: 0 0 1rem; color: #fb923c; font-size: 0.9rem; display: flex; align-items: center; gap: 0.5rem; } +.git-section { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid rgba(255,255,255,0.1); } +.git-section h4 { margin: 0 0 1rem; font-size: 0.9rem; display: flex; align-items: center; gap: 0.5rem; } +.git-section.gitlab h4 { color: #fb923c; } +.git-section.gitea h4 { color: #22c55e; } -.gitlab-multi-select { max-height: 150px; overflow-y: auto; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; background: rgba(255,255,255,0.02); } -.gitlab-option { display: flex; align-items: center; gap: 0.75rem; padding: 0.5rem 0.75rem; cursor: pointer; transition: background 0.2s; border-bottom: 1px solid rgba(255,255,255,0.05); } -.gitlab-option:last-child { border-bottom: none; } -.gitlab-option:hover { background: rgba(255,255,255,0.05); } -.gitlab-option.selected { background: rgba(99,102,241,0.2); } +.git-multi-select { max-height: 150px; overflow-y: auto; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; background: rgba(255,255,255,0.02); } +.git-option { display: flex; align-items: center; gap: 0.75rem; padding: 0.5rem 0.75rem; cursor: pointer; transition: background 0.2s; border-bottom: 1px solid rgba(255,255,255,0.05); } +.git-option:last-child { border-bottom: none; } +.git-option:hover { background: rgba(255,255,255,0.05); } +.git-option.selected { background: rgba(99,102,241,0.2); } .checkbox-icon { color: rgba(255,255,255,0.4); font-size: 1rem; } -.gitlab-option.selected .checkbox-icon { color: #6366f1; } -.gitlab-name { font-size: 0.8rem; color: rgba(255,255,255,0.9); } +.git-option.selected .checkbox-icon { color: #6366f1; } +.git-name { font-size: 0.8rem; color: rgba(255,255,255,0.9); } +.git-info { margin-top: 0.5rem; padding: 0.5rem; background: rgba(99,102,241,0.1); border-radius: 8px; font-size: 0.75rem; color: #818cf8; }