|
|
|
@ -11,24 +11,25 @@ |
|
|
|
close-on-click-outside |
|
|
|
out-transition |
|
|
|
@update:open="onCancel"> |
|
|
|
<NcEmptyContent v-if="loading || done" :name="t('systemtags', 'Applying tags changes…')"> |
|
|
|
<NcEmptyContent v-if="status === Status.LOADING || status === Status.DONE" |
|
|
|
:name="t('systemtags', 'Applying tags changes…')"> |
|
|
|
<template #icon> |
|
|
|
<NcLoadingIcon v-if="!done" /> |
|
|
|
<NcLoadingIcon v-if="status === Status.LOADING" /> |
|
|
|
<CheckIcon v-else fill-color="var(--color-success)" /> |
|
|
|
</template> |
|
|
|
</NcEmptyContent> |
|
|
|
|
|
|
|
<template v-else> |
|
|
|
<!-- Search or create input --> |
|
|
|
<div class="systemtags-picker__create"> |
|
|
|
<form class="systemtags-picker__create" @submit.stop.prevent="onNewTag"> |
|
|
|
<NcTextField :value.sync="input" |
|
|
|
:label="t('systemtags', 'Search or create tag')"> |
|
|
|
<TagIcon :size="20" /> |
|
|
|
</NcTextField> |
|
|
|
<NcButton> |
|
|
|
<NcButton :disabled="status === Status.CREATING_TAG" native-type="submit"> |
|
|
|
{{ t('systemtags', 'Create tag') }} |
|
|
|
</NcButton> |
|
|
|
</div> |
|
|
|
</form> |
|
|
|
|
|
|
|
<!-- Tags list --> |
|
|
|
<div v-if="filteredTags.length > 0" class="systemtags-picker__tags"> |
|
|
|
@ -60,10 +61,10 @@ |
|
|
|
</template> |
|
|
|
|
|
|
|
<template #actions> |
|
|
|
<NcButton :disabled="loading || done" type="tertiary" @click="onCancel"> |
|
|
|
<NcButton :disabled="status !== Status.BASE" type="tertiary" @click="onCancel"> |
|
|
|
{{ t('systemtags', 'Cancel') }} |
|
|
|
</NcButton> |
|
|
|
<NcButton :disabled="!hasChanges || loading || done" @click="onSubmit"> |
|
|
|
<NcButton :disabled="!hasChanges || status !== Status.BASE" @click="onSubmit"> |
|
|
|
{{ t('systemtags', 'Apply changes') }} |
|
|
|
</NcButton> |
|
|
|
</template> |
|
|
|
@ -81,7 +82,7 @@ |
|
|
|
<script lang="ts"> |
|
|
|
import type { Node } from '@nextcloud/files' |
|
|
|
import type { PropType } from 'vue' |
|
|
|
import type { TagWithId } from '../types' |
|
|
|
import type { Tag, TagWithId } from '../types' |
|
|
|
|
|
|
|
import { defineComponent } from 'vue' |
|
|
|
import { emit } from '@nextcloud/event-bus' |
|
|
|
@ -102,13 +103,20 @@ import TagIcon from 'vue-material-design-icons/Tag.vue' |
|
|
|
import CheckIcon from 'vue-material-design-icons/CheckCircle.vue' |
|
|
|
|
|
|
|
import { getNodeSystemTags, setNodeSystemTags } from '../utils' |
|
|
|
import { getTagObjects, setTagObjects } from '../services/api' |
|
|
|
import { createTag, fetchTag, fetchTags, getTagObjects, setTagObjects } from '../services/api' |
|
|
|
import logger from '../services/logger' |
|
|
|
|
|
|
|
type TagListCount = { |
|
|
|
string: number |
|
|
|
} |
|
|
|
|
|
|
|
enum Status { |
|
|
|
BASE, |
|
|
|
LOADING, |
|
|
|
CREATING_TAG, |
|
|
|
DONE, |
|
|
|
} |
|
|
|
|
|
|
|
export default defineComponent({ |
|
|
|
name: 'SystemTagPicker', |
|
|
|
|
|
|
|
@ -131,27 +139,23 @@ export default defineComponent({ |
|
|
|
type: Array as PropType<Node[]>, |
|
|
|
required: true, |
|
|
|
}, |
|
|
|
|
|
|
|
tags: { |
|
|
|
type: Array as PropType<TagWithId[]>, |
|
|
|
default: () => [], |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
setup() { |
|
|
|
return { |
|
|
|
emit, |
|
|
|
Status, |
|
|
|
t, |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
data() { |
|
|
|
return { |
|
|
|
done: false, |
|
|
|
loading: false, |
|
|
|
status: Status.BASE, |
|
|
|
opened: true, |
|
|
|
|
|
|
|
input: '', |
|
|
|
tags: [] as TagWithId[], |
|
|
|
tagList: {} as TagListCount, |
|
|
|
|
|
|
|
toAdd: [] as TagWithId[], |
|
|
|
@ -243,6 +247,10 @@ export default defineComponent({ |
|
|
|
}, |
|
|
|
|
|
|
|
beforeMount() { |
|
|
|
fetchTags().then(tags => { |
|
|
|
this.tags = tags |
|
|
|
}) |
|
|
|
|
|
|
|
// Efficient way of counting tags and their occurrences |
|
|
|
this.tagList = this.nodes.reduce((acc: TagListCount, node: Node) => { |
|
|
|
const tags = getNodeSystemTags(node) || [] |
|
|
|
@ -296,8 +304,28 @@ export default defineComponent({ |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
async onNewTag() { |
|
|
|
this.status = Status.CREATING_TAG |
|
|
|
try { |
|
|
|
const payload: Tag = { |
|
|
|
displayName: this.input.trim(), |
|
|
|
userAssignable: true, |
|
|
|
userVisible: true, |
|
|
|
canAssign: true, |
|
|
|
} |
|
|
|
const id = await createTag(payload) |
|
|
|
const tag = await fetchTag(id) |
|
|
|
this.tags.push(tag) |
|
|
|
this.input = '' |
|
|
|
} catch (error) { |
|
|
|
showError((error as Error)?.message || t('systemtags', 'Failed to create tag')) |
|
|
|
} finally { |
|
|
|
this.status = Status.BASE |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
async onSubmit() { |
|
|
|
this.loading = true |
|
|
|
this.status = Status.LOADING |
|
|
|
logger.debug('Applying tags', { |
|
|
|
toAdd: this.toAdd, |
|
|
|
toRemove: this.toRemove, |
|
|
|
@ -336,7 +364,7 @@ export default defineComponent({ |
|
|
|
} catch (error) { |
|
|
|
logger.error('Failed to apply tags', { error }) |
|
|
|
showError(t('systemtags', 'Failed to apply tags changes')) |
|
|
|
this.loading = false |
|
|
|
this.status = Status.BASE |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
@ -364,8 +392,7 @@ export default defineComponent({ |
|
|
|
// trigger update event |
|
|
|
nodes.forEach(node => emit('systemtags:node:updated', node)) |
|
|
|
|
|
|
|
this.done = true |
|
|
|
this.loading = false |
|
|
|
this.status = Status.DONE |
|
|
|
setTimeout(() => { |
|
|
|
this.opened = false |
|
|
|
this.$emit('close', null) |
|
|
|
|