diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ea21a4e..fc53655 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -55,4 +55,5 @@ deploy: - ssh -p "$DEPLOY_SSH_PORT" "$DEPLOYER_USER@$DEPLOYER_HOST" "docker stop '$CONTAINER_NAME' 2>/dev/null || true" - ssh -p "$DEPLOY_SSH_PORT" "$DEPLOYER_USER@$DEPLOYER_HOST" "docker rm '$CONTAINER_NAME' 2>/dev/null || true" - ssh -p "$DEPLOY_SSH_PORT" "$DEPLOYER_USER@$DEPLOYER_HOST" "docker run -d --name '$CONTAINER_NAME' --restart unless-stopped -p '$HOST_PORT:$CONTAINER_PORT' '$IMAGE_BASE:$CI_COMMIT_SHORT_SHA'" + - ssh -p "$DEPLOY_SSH_PORT" "$DEPLOYER_USER@$DEPLOYER_HOST" "docker images '$IMAGE_BASE' --format '{{.Repository}}:{{.Tag}}' | grep -v -e ':latest$' -e ':$CI_COMMIT_SHORT_SHA$' | xargs -r docker rmi || true" - ssh -p "$DEPLOY_SSH_PORT" "$DEPLOYER_USER@$DEPLOYER_HOST" "docker logout" diff --git a/src/views/projects/ProjectsListView.vue b/src/views/projects/ProjectsListView.vue index bb34b13..1a91c30 100644 --- a/src/views/projects/ProjectsListView.vue +++ b/src/views/projects/ProjectsListView.vue @@ -71,11 +71,42 @@ {{ formatDate(data.updatedAt) }} - + {{ data.updatedByName || '—' }} + + + + + + + + + @@ -94,57 +125,20 @@ - + - Name - + Name + - Description - - - - - Status - - - - Created - {{ formatDate(selectedProject.createdAt) }} - - - Updated - {{ formatDate(selectedProject.updatedAt) }} - + Description + - - - - - - - - - - - + + @@ -176,8 +170,6 @@ const statusOptions = [ { label: 'Active', value: 'Active' }, { label: 'Archived', value: 'Archived' }, ] -const selectedProject = ref(null) - const page = ref(1) const pageSize = ref(20) const totalCount = ref(0) @@ -188,12 +180,13 @@ const newName = ref('') const newDescription = ref('') const creating = ref(false) -const detailDialog = ref(false) +const editDialog = ref(false) +const editingProject = ref(null) const editName = ref('') const editDescription = ref('') const saving = ref(false) -const archiving = ref(false) -const deleting = ref(false) +const archivingId = ref(null) +const deletingId = ref(null) let textTimer: ReturnType | undefined @@ -241,43 +234,40 @@ function notImplemented(feature: string) { } function onRowClick(event: DataTableRowClickEvent) { - if (event.data) openDetail(event.data as Project) + if (event.data) openProject(event.data as Project) } -function openDetail(project: Project) { - selectedProject.value = project +function openProject(project: Project) { + localStorage.setItem('mws_last_project', project.id) + router.push({ name: 'tasks-board', params: { id: project.id } }) +} + +function openEdit(project: Project) { + editingProject.value = project editName.value = project.name editDescription.value = project.description ?? '' - detailDialog.value = true -} - -function openProject() { - if (!selectedProject.value) return - localStorage.setItem('mws_last_project', selectedProject.value.id) - router.push({ name: 'tasks-board', params: { id: selectedProject.value.id } }) + editDialog.value = true } async function onSave() { - if (!selectedProject.value) return + if (!editingProject.value) return if (!editName.value) { toast.add({ severity: 'warn', summary: 'Name is required', life: 3000 }) return } saving.value = true try { - const updated = await updateProject(selectedProject.value.id, { + const updated = await updateProject(editingProject.value.id, { name: editName.value, description: editDescription.value || null, - status: selectedProject.value.status, + status: editingProject.value.status, }) - selectedProject.value.name = updated.name - selectedProject.value.description = updated.description const idx = projects.value.findIndex((p) => p.id === updated.id) if (idx !== -1) { projects.value[idx].name = updated.name projects.value[idx].description = updated.description } - detailDialog.value = false + editDialog.value = false toast.add({ severity: 'success', summary: 'Project updated', life: 3000 }) } catch (e) { toast.add({ severity: 'error', summary: 'Error', detail: errorMessage(e), life: 4000 }) @@ -286,17 +276,15 @@ async function onSave() { } } -async function onArchive() { - if (!selectedProject.value) return - const next = selectedProject.value.status === 'Archived' ? 'Active' : 'Archived' - archiving.value = true +async function onArchive(project: Project) { + const next = project.status === 'Archived' ? 'Active' : 'Archived' + archivingId.value = project.id try { - const updated = await updateProject(selectedProject.value.id, { - name: selectedProject.value.name, - description: selectedProject.value.description ?? '', + const updated = await updateProject(project.id, { + name: project.name, + description: project.description ?? '', status: next, }) - selectedProject.value.status = updated.status const idx = projects.value.findIndex((p) => p.id === updated.id) if (idx !== -1) projects.value[idx].status = updated.status toast.add({ @@ -307,13 +295,11 @@ async function onArchive() { } catch (e) { toast.add({ severity: 'error', summary: 'Error', detail: errorMessage(e), life: 4000 }) } finally { - archiving.value = false + archivingId.value = null } } -function onDelete() { - if (!selectedProject.value) return - const target = selectedProject.value +function onDelete(target: Project) { confirm.require({ message: `Delete "${target.name}"? This cannot be undone.`, header: 'Delete Project', @@ -321,7 +307,7 @@ function onDelete() { acceptProps: { severity: 'danger' }, rejectProps: { severity: 'secondary', outlined: true }, accept: async () => { - deleting.value = true + deletingId.value = target.id try { await deleteProject(target.id) projects.value = projects.value.filter((p) => p.id !== target.id) @@ -329,12 +315,11 @@ function onDelete() { if (localStorage.getItem('mws_last_project') === target.id) { localStorage.removeItem('mws_last_project') } - detailDialog.value = false toast.add({ severity: 'success', summary: 'Project deleted', life: 3000 }) } catch (e) { toast.add({ severity: 'error', summary: 'Error', detail: errorMessage(e), life: 4000 }) } finally { - deleting.value = false + deletingId.value = null } }, })
Status
Created
{{ formatDate(selectedProject.createdAt) }}
Updated
{{ formatDate(selectedProject.updatedAt) }}