feat: Tag dropdown visual and functional enhancements Visual and functional improvements have been made to the tag dropdown for a more intuitive and visually appealing experience. A red 'Delete tag' button has been added, providing functionality to open a modal for selecting and deleting tags. Additionally, Turkish translations for the newly added texts (Delete tag, Select tags to delete:) have been added to the tr-TR.json file.

This commit is contained in:
benemiryilmazer 2025-06-09 17:20:17 +03:00
parent c46772dafc
commit 8cc5a05be4
4 changed files with 316 additions and 8 deletions

260
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "2.0.0-beta.2", "version": "2.0.0-beta.3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "2.0.0-beta.2", "version": "2.0.0-beta.3",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@grpc/grpc-js": "~1.8.22", "@grpc/grpc-js": "~1.8.22",
@ -83,6 +83,7 @@
"tcp-ping": "~0.1.1", "tcp-ping": "~0.1.1",
"thirty-two": "~1.0.2", "thirty-two": "~1.0.2",
"tough-cookie": "~4.1.3", "tough-cookie": "~4.1.3",
"vue-grid-layout": "^2.4.0",
"ws": "^8.13.0" "ws": "^8.13.0"
}, },
"devDependencies": { "devDependencies": {
@ -2475,6 +2476,231 @@
"dev": true, "dev": true,
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/@interactjs/actions": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/actions/-/actions-1.10.2.tgz",
"integrity": "sha512-BHJcW84WCMf/LsKmha/1Yog7aH3+QBXbLvowvZvwYvgjdUIb3xCa1a7FUYXuWAeKNMyKPVjFun+WPce75B+1tA==",
"license": "MIT",
"optionalDependencies": {
"@interactjs/interact": "1.10.2"
},
"peerDependencies": {
"@interactjs/core": "1.10.2",
"@interactjs/utils": "1.10.2"
}
},
"node_modules/@interactjs/arrange": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/arrange/-/arrange-1.10.2.tgz",
"integrity": "sha512-pPLA9o4RWMFN0VfalklOFSRLL4WqqXcD9no4XEuqV00goZPCxLBbMTztaWwnutlRy7evtOhUjUH+pZVsS+dZ4Q==",
"license": "MIT"
},
"node_modules/@interactjs/auto-scroll": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/auto-scroll/-/auto-scroll-1.10.2.tgz",
"integrity": "sha512-yYqzOawwvWd1NNnlqZdzrXoOMFafQ2/ws85erpJqdaNMQE221z2uP+QYhFRLQRgYUlTbHFfmjDpzhuJgq4uA8Q==",
"license": "MIT",
"optionalDependencies": {
"@interactjs/interact": "1.10.2"
},
"peerDependencies": {
"@interactjs/utils": "1.10.2"
}
},
"node_modules/@interactjs/auto-start": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/auto-start/-/auto-start-1.10.2.tgz",
"integrity": "sha512-nZudj8VzJzz+uEyDHqXwtKpvUYr+Oj1+xBrJEu21CywroHQWM2J4fCIiCgeCo3d5/p/TrzFk5b+YfAWePKiLxA==",
"license": "MIT",
"optionalDependencies": {
"@interactjs/interact": "1.10.2"
},
"peerDependencies": {
"@interactjs/core": "1.10.2",
"@interactjs/utils": "1.10.2"
}
},
"node_modules/@interactjs/clone": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/clone/-/clone-1.10.2.tgz",
"integrity": "sha512-XzA8BRHSCwvysOegZ1kopg+IJF3erh4qzY6DRoZsIJovKAXawoa176E58IAzDbgYPJ2yoaSGT+XyzT2C0wa3pQ==",
"license": "MIT"
},
"node_modules/@interactjs/core": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/core/-/core-1.10.2.tgz",
"integrity": "sha512-SA5KRGo+gFJOhBj1Z2dLHhAf0/2nyHNd4SQ460aIQ3jj/QhqbJW6kGzmh7hBa2FzVGgxLhcQu7NZaP4rnDfUNw==",
"license": "MIT",
"peerDependencies": {
"@interactjs/utils": "1.10.2"
}
},
"node_modules/@interactjs/dev-tools": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/dev-tools/-/dev-tools-1.10.2.tgz",
"integrity": "sha512-aAd9NgTAGA3yVdFCYcAAYrM4TYQFuVqEvsF+xj+g5SlGyrJ7+GTjPZ2rScOyAsABY4Tz64L2pXvWmXMG87dncA==",
"license": "MIT",
"dependencies": {
"@interactjs/utils": "1.10.2"
},
"optionalDependencies": {
"@interactjs/interact": "1.10.2"
}
},
"node_modules/@interactjs/feedback": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/feedback/-/feedback-1.10.2.tgz",
"integrity": "sha512-XlcoICGrFeUwwRtDgOpstOOvlU42WZoEg7gJHK3LwF7j0IctPd1+3blXofFlBeVvodle8MvUMepm5CRXz741fA==",
"license": "MIT"
},
"node_modules/@interactjs/inertia": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/inertia/-/inertia-1.10.2.tgz",
"integrity": "sha512-ZmN1joN6J36Q6SOp3V0iZOisXZOBMSAUj0STo8wbwCKy7K8IrC9vjUBbO2JM52cT6o7hg5ebHsp5c8FrebSHlg==",
"license": "MIT",
"dependencies": {
"@interactjs/offset": "1.10.2"
},
"optionalDependencies": {
"@interactjs/interact": "1.10.2"
},
"peerDependencies": {
"@interactjs/core": "1.10.2",
"@interactjs/modifiers": "1.10.2",
"@interactjs/utils": "1.10.2"
}
},
"node_modules/@interactjs/interact": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/interact/-/interact-1.10.2.tgz",
"integrity": "sha512-Ms5uVCY9IobVYpQyBnBdkP6Bk6iDY7TkC7GupsdUPUxzAvYSQCTEAGr/1PwxSrSS6dN/8O8TuyUWPbCaylr/JA==",
"license": "MIT",
"dependencies": {
"@interactjs/core": "1.10.2",
"@interactjs/types": "1.10.2",
"@interactjs/utils": "1.10.2"
}
},
"node_modules/@interactjs/interactjs": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/interactjs/-/interactjs-1.10.2.tgz",
"integrity": "sha512-OwLl70af6lfZOOg/bvWKSNm1DS1nDI72QnzDYljSKfc2D8stqLIGDO1wPY2rhZudUG5q3t50EhmMUQF76yll/g==",
"license": "MIT",
"dependencies": {
"@interactjs/actions": "1.10.2",
"@interactjs/arrange": "1.10.2",
"@interactjs/auto-scroll": "1.10.2",
"@interactjs/auto-start": "1.10.2",
"@interactjs/clone": "1.10.2",
"@interactjs/core": "1.10.2",
"@interactjs/dev-tools": "1.10.2",
"@interactjs/feedback": "1.10.2",
"@interactjs/inertia": "1.10.2",
"@interactjs/interact": "1.10.2",
"@interactjs/modifiers": "1.10.2",
"@interactjs/multi-target": "1.10.2",
"@interactjs/offset": "1.10.2",
"@interactjs/pointer-events": "1.10.2",
"@interactjs/react": "1.10.2",
"@interactjs/reflow": "1.10.2",
"@interactjs/utils": "1.10.2",
"@interactjs/vue": "1.10.2"
}
},
"node_modules/@interactjs/modifiers": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/modifiers/-/modifiers-1.10.2.tgz",
"integrity": "sha512-3wYEucvZF2NTIslnVIKw5MWhkn9LM42cGCQaC19o3LZeWnbps7NnHJCyQp6zylJrCbwt7f+CSt4Oj2/s0f6XEA==",
"license": "MIT",
"dependencies": {
"@interactjs/snappers": "1.10.2"
},
"optionalDependencies": {
"@interactjs/interact": "1.10.2"
},
"peerDependencies": {
"@interactjs/core": "1.10.2",
"@interactjs/utils": "1.10.2"
}
},
"node_modules/@interactjs/multi-target": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/multi-target/-/multi-target-1.10.2.tgz",
"integrity": "sha512-O2GiIqgZBzjAVTOpL8doTnAcM9AtM3+H/Bb+An12wWKtNutVK7JbqUAO2nYueOk55/PP3yDLY9Qdr15RJns3lQ==",
"license": "MIT"
},
"node_modules/@interactjs/offset": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/offset/-/offset-1.10.2.tgz",
"integrity": "sha512-xLgQqinFUY7ZqSX9d9on7XRcxvQdHNEAktj2QFwxMsEwrA6zbKROpPVwt8WQ1yBAeJSFjgYGcmCMPW5K41dT0w==",
"license": "MIT",
"optionalDependencies": {
"@interactjs/interact": "1.10.2"
},
"peerDependencies": {
"@interactjs/core": "1.10.2",
"@interactjs/utils": "1.10.2"
}
},
"node_modules/@interactjs/pointer-events": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/pointer-events/-/pointer-events-1.10.2.tgz",
"integrity": "sha512-O8s3N399hkGIzWGlcJVy0LJyDn5YWDh6XKjyowh/QivtlZSWPY8eglmlN2uZX0lmiqUYghbKI4CpQYP/cE0ZDA==",
"license": "MIT",
"optionalDependencies": {
"@interactjs/interact": "1.10.2"
},
"peerDependencies": {
"@interactjs/core": "1.10.2",
"@interactjs/utils": "1.10.2"
}
},
"node_modules/@interactjs/react": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/react/-/react-1.10.2.tgz",
"integrity": "sha512-JXzPdANft+W2vq3SCSzprCwom5UuC8TaiAAhVdt8R+/P6xHbOeAX93XLS5YmDwT8e0Zh9J9jYvz55tkTdwjFZQ==",
"license": "MIT"
},
"node_modules/@interactjs/reflow": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/reflow/-/reflow-1.10.2.tgz",
"integrity": "sha512-pc6o6RRhSCYQC4auZexRb7z5FQkdSVev5HzlRfUAjfw4C076qgbcs63ESRKy4YXdSBtUTvARQZxpuWUNGquzJw==",
"license": "MIT",
"optionalDependencies": {
"@interactjs/interact": "1.10.2"
},
"peerDependencies": {
"@interactjs/core": "1.10.2",
"@interactjs/utils": "1.10.2"
}
},
"node_modules/@interactjs/snappers": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/snappers/-/snappers-1.10.2.tgz",
"integrity": "sha512-wQ41Vn5GRn6VavjIEUtTkd9d5QgdKgC4+CPW0fjKkiQclLBmaic7VibNETO8twN0Jx5e73EoPj9K2nAVHIA0hA==",
"license": "MIT",
"peerDependencies": {
"@interactjs/utils": "1.10.2"
}
},
"node_modules/@interactjs/types": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/types/-/types-1.10.2.tgz",
"integrity": "sha512-l0T1bU8OHRv716ztQOYwP+K7b/lA76C0T3r/cdabbUv6CKeAFdFRRFlmNxYOM36SxMGWAiq5VWrN3SeXlB7Fow==",
"license": "MIT"
},
"node_modules/@interactjs/utils": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/utils/-/utils-1.10.2.tgz",
"integrity": "sha512-sOr+pu7XGAN4qv+ikajMo3RJygbkbMLegmx0Tv5Qf6e80sF42FjkmHeMGuV7fL98nwat0VmDiWerOFBnKctXow==",
"license": "MIT"
},
"node_modules/@interactjs/vue": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@interactjs/vue/-/vue-1.10.2.tgz",
"integrity": "sha512-msLdc42DFsCPQZt6YBGZrw8Ro23kQcNnj+iLz2OUQcOrp/lma7WjorUuAwwfyFna2DevLtiYlMLbT0dpO/cVhg==",
"license": "MIT"
},
"node_modules/@intlify/core-base": { "node_modules/@intlify/core-base": {
"version": "9.14.4", "version": "9.14.4",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.4.tgz", "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.4.tgz",
@ -6059,6 +6285,12 @@
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/batch-processor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz",
"integrity": "sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==",
"license": "MIT"
},
"node_modules/bcrypt-pbkdf": { "node_modules/bcrypt-pbkdf": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
@ -8084,6 +8316,15 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/element-resize-detector": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/element-resize-detector/-/element-resize-detector-1.2.4.tgz",
"integrity": "sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==",
"license": "MIT",
"dependencies": {
"batch-processor": "1.0.0"
}
},
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@ -17418,6 +17659,21 @@
"eslint": ">=6.0.0" "eslint": ">=6.0.0"
} }
}, },
"node_modules/vue-grid-layout": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/vue-grid-layout/-/vue-grid-layout-2.4.0.tgz",
"integrity": "sha512-MRQVt1BdWDaPN4gKGEKOVVwiTyucqJhrGEyjiY9Muor+dzFFq4Hm0geSpYJpLvC1GLlTL8KWUwy0suKrHm+mqg==",
"license": "MIT",
"dependencies": {
"@interactjs/actions": "1.10.2",
"@interactjs/auto-scroll": "1.10.2",
"@interactjs/auto-start": "1.10.2",
"@interactjs/dev-tools": "1.10.2",
"@interactjs/interactjs": "1.10.2",
"@interactjs/modifiers": "1.10.2",
"element-resize-detector": "^1.2.1"
}
},
"node_modules/vue-i18n": { "node_modules/vue-i18n": {
"version": "9.14.4", "version": "9.14.4",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.4.tgz", "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.4.tgz",

View file

@ -141,6 +141,7 @@
"tcp-ping": "~0.1.1", "tcp-ping": "~0.1.1",
"thirty-two": "~1.0.2", "thirty-two": "~1.0.2",
"tough-cookie": "~4.1.3", "tough-cookie": "~4.1.3",
"vue-grid-layout": "^2.4.0",
"ws": "^8.13.0" "ws": "^8.13.0"
}, },
"devDependencies": { "devDependencies": {

View file

@ -129,9 +129,9 @@
</template> </template>
<template #dropdown> <template #dropdown>
<li v-for="tag in tagsList" :key="tag.id"> <li v-for="tag in tagsList" :key="tag.id">
<div class="dropdown-item" tabindex="0" @click.stop="toggleTagFilter(tag)"> <div class="dropdown-item tag-dropdown-item" tabindex="0" @click.stop="toggleTagFilter(tag)">
<div class="d-flex align-items-center justify-content-between"> <div class="d-flex align-items-center justify-content-between">
<span><Tag :item="tag" :size="'sm'" /></span> <Tag :item="tag" :size="'sm'" />
<span class="ps-3"> <span class="ps-3">
{{ getTaggedMonitorCount(tag) }} {{ getTaggedMonitorCount(tag) }}
<span v-if="filterState.tags?.includes(tag.id)" class="px-1 filter-active"> <span v-if="filterState.tags?.includes(tag.id)" class="px-1 filter-active">
@ -141,14 +141,50 @@
</div> </div>
</div> </div>
</li> </li>
<li v-if="tagsList.length === 0"> <li v-if="tagsList.length === 0" class="no-tags-found">
<div class="dropdown-item disabled px-3"> <div class="dropdown-item disabled px-3 text-center">
{{ $t('No tags found.') }} {{ $t('No tags found.') }}
</div> </div>
</li> </li>
<li v-if="tagsList.length > 0">
<hr class="dropdown-divider">
<div class="dropdown-item px-3 py-2">
<button class="btn btn-danger w-100 delete-tag-button" @click.stop="openDeleteTagModal = true">{{ $t('Delete tag') }}</button>
</div>
</li>
</template> </template>
</MonitorListFilterDropdown> </MonitorListFilterDropdown>
</div> </div>
<!-- Modal -->
<div v-if="openDeleteTagModal" class="modal-backdrop">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ $t('Delete tag') }}</h5>
<button type="button" class="btn-close" @click="openDeleteTagModal = false"></button>
</div>
<div class="modal-body">
<div v-if="tagsList.length > 0">
<label class="form-label">{{ $t('Select tags to delete:') }}</label>
<div v-for="tag in tagsList" :key="tag.id" class="form-check tag-delete-checkbox">
<input class="form-check-input" type="checkbox" :id="'delete-tag-' + tag.id" v-model="selectedTagsToDelete" :value="tag.id">
<label class="form-check-label" :for="'delete-tag-' + tag.id">
<Tag :item="tag" :size="'sm'" />
</label>
</div>
</div>
<div v-else class="text-center text-muted">
{{ $t('No tags found.') }}
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="openDeleteTagModal = false">{{ $t('Cancel') }}</button>
<button type="button" class="btn btn-danger" :disabled="selectedTagsToDelete.length === 0" @click="deleteSelectedTags">{{ $t('Delete') }}</button>
</div>
</div>
</div>
</div>
</template> </template>
<script> <script>
@ -172,6 +208,8 @@ export default {
data() { data() {
return { return {
tagsList: [], tagsList: [],
openDeleteTagModal: false,
selectedTagsToDelete: [],
}; };
}, },
computed: { computed: {
@ -255,7 +293,18 @@ export default {
return Object.values(this.$root.monitorList).filter(monitor => { return Object.values(this.$root.monitorList).filter(monitor => {
return monitor.tags.find(monitorTag => monitorTag.tag_id === tag.id); return monitor.tags.find(monitorTag => monitorTag.tag_id === tag.id);
}).length; }).length;
} },
async deleteSelectedTags() {
for (const tagId of this.selectedTagsToDelete) {
await new Promise((resolve) => {
this.$root.getSocket().emit('deleteTag', tagId, resolve);
});
}
this.openDeleteTagModal = false;
this.selectedTagsToDelete = [];
this.getExistingTags();
this.$root.toastSuccess(this.$t('successDeleted'));
},
} }
}; };
</script> </script>

View file

@ -1117,5 +1117,7 @@
"Template Format": "Şablon Biçimi", "Template Format": "Şablon Biçimi",
"YZJ Webhook URL": "YZJ Webhook URL'si", "YZJ Webhook URL": "YZJ Webhook URL'si",
"YZJ Robot Token": "YZJ Robot tokeni", "YZJ Robot Token": "YZJ Robot tokeni",
"telegramServerUrl": "(İsteğe bağlı) Sunucu URL'si" "telegramServerUrl": "(İsteğe bağlı) Sunucu URL'si",
"Delete tag": "Etiketi Sil",
"Select tags to delete:": "Silmek istediğiniz etiketleri seçin:"
} }