+ class="entry-item" :class="{ selected: selectedIds.includes(entry.id) }"
+ @click="openEntry(entry)">
+
@@ -166,6 +179,7 @@ import InputText from 'primevue/inputtext'
import Password from 'primevue/password'
import Textarea from 'primevue/textarea'
import Select from 'primevue/select'
+import Checkbox from 'primevue/checkbox'
const toast = useToast()
const auth = useAuthStore()
@@ -200,6 +214,45 @@ const importAccept = computed(() => {
const showTotpDialog = ref(false)
const totpCode = ref('')
+const selectedIds = ref([])
+const allSelected = computed({
+ get: () => filteredEntries.value.length > 0 && filteredEntries.value.every(e => selectedIds.value.includes(e.id)),
+ set: () => {},
+})
+
+function toggleSelectAll() {
+ const visibleIds = filteredEntries.value.map(e => e.id)
+ const allSel = visibleIds.every(id => selectedIds.value.includes(id))
+ if (allSel) {
+ selectedIds.value = selectedIds.value.filter(id => !visibleIds.includes(id))
+ } else {
+ const set = new Set([...selectedIds.value, ...visibleIds])
+ selectedIds.value = [...set]
+ }
+}
+
+function toggleSelect(id) {
+ const i = selectedIds.value.indexOf(id)
+ if (i >= 0) selectedIds.value.splice(i, 1)
+ else selectedIds.value.push(id)
+}
+
+async function deleteSelected() {
+ const n = selectedIds.value.length
+ if (!n) return
+ if (!window.confirm(`${n} Eintrag/Eintraege wirklich loeschen?`)) return
+ let ok = 0
+ for (const id of [...selectedIds.value]) {
+ try {
+ await apiClient.delete(`/passwords/entries/${id}`)
+ ok++
+ } catch { /* skip */ }
+ }
+ selectedIds.value = []
+ toast.add({ severity: 'success', summary: `${ok} Eintrag/Eintraege geloescht`, life: 3000 })
+ await loadEntries()
+}
+
const folderOptions = computed(() => [{ id: null, name: '(Kein Ordner)' }, ...folders.value])
const filteredEntries = computed(() => {
if (!searchQuery.value) return entries.value
@@ -491,6 +544,10 @@ onMounted(async () => {
.shared-label { color: var(--p-text-muted-color); font-size: 0.75rem; }
.entries-main { flex: 1; }
.search-bar { margin-bottom: 1rem; }
+.selection-bar { display: flex; align-items: center; gap: 0.75rem; padding: 0.5rem 0.75rem; margin-bottom: 0.5rem; background: var(--p-surface-50); border-radius: 6px; }
+.select-all-label { font-size: 0.875rem; cursor: pointer; flex: 1; }
+.selected-count { color: var(--p-text-muted-color); margin-left: 0.5rem; }
+.entry-item.selected { background: var(--p-primary-50); }
.entries-list { display: flex; flex-direction: column; gap: 2px; }
.entry-item { display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem; background: var(--p-surface-0); border-radius: 6px; cursor: pointer; }
.entry-item:hover { background: var(--p-surface-100); }