feat: port label store to pinia | pinia 1/9 #2391
|
@ -46,6 +46,7 @@
|
|||
"lodash.debounce": "4.0.8",
|
||||
"marked": "4.1.0",
|
||||
"minimist": "1.2.6",
|
||||
"pinia": "^2.0.21",
|
||||
"register-service-worker": "1.7.2",
|
||||
"snake-case": "3.0.4",
|
||||
"ufo": "0.8.5",
|
||||
|
|
|
@ -66,6 +66,7 @@ import {useRoute, useRouter} from 'vue-router'
|
|||
import {useEventListener} from '@vueuse/core'
|
||||
|
||||
import {CURRENT_LIST, KEYBOARD_SHORTCUTS_ACTIVE, MENU_ACTIVE} from '@/store/mutation-types'
|
||||
import {useLabelStore} from '@/stores/labels'
|
||||
import Navigation from '@/components/home/navigation.vue'
|
||||
import QuickActions from '@/components/quick-actions/quick-actions.vue'
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
|
@ -197,7 +198,8 @@ function useRenewTokenOnFocus() {
|
|||
}
|
||||
|
||||
useRenewTokenOnFocus()
|
||||
store.dispatch('labels/loadAllLabels')
|
||||
const labelStore = useLabelStore()
|
||||
labelStore.loadAllLabels()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -190,6 +190,8 @@
|
|||
<script lang="ts">
|
||||
import {defineComponent} from 'vue'
|
||||
|
||||
import {useLabelStore} from '@/stores/labels'
|
||||
|
||||
import DatepickerWithRange from '@/components/date/datepickerWithRange.vue'
|
||||
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
||||
|
||||
|
@ -307,8 +309,10 @@ export default defineComponent({
|
|||
this.change()
|
||||
},
|
||||
},
|
||||
|
||||
foundLabels() {
|
||||
return this.$store.getters['labels/filterLabelsByQuery'](this.labels, this.query)
|
||||
const labelStore = useLabelStore()
|
||||
dpschen marked this conversation as resolved
|
||||
return labelStore.filterLabelsByQuery(this.labels, this.labelQuery)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
@ -336,7 +340,8 @@ export default defineComponent({
|
|||
: ''
|
||||
const labelIds = labels.split(',').map(i => parseInt(i))
|
||||
|
||||
this.labels = this.$store.getters['labels/getLabelsByIds'](labelIds)
|
||||
const labelStore = useLabelStore()
|
||||
this.labels = labelStore.getLabelsByIds(labelIds)
|
||||
},
|
||||
removePropertyFromFilter(propertyName) {
|
||||
// Because of the way arrays work, we can only ever remove one element at once.
|
||||
|
|
|
@ -50,6 +50,7 @@ import {success} from '@/message'
|
|||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import Multiselect from '@/components/input/multiselect.vue'
|
||||
import type { ILabel } from '@/modelTypes/ILabel'
|
||||
import { useLabelStore } from '@/stores/labels'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
|
@ -86,8 +87,10 @@ watch(
|
|||
},
|
||||
)
|
||||
|
||||
const foundLabels = computed(() => store.getters['labels/filterLabelsByQuery'](labels.value, query.value))
|
||||
const loading = computed(() => labelTaskService.loading || (store.state.loading && store.state.loadingModule === 'labels'))
|
||||
const labelStore = useLabelStore()
|
||||
|
||||
const foundLabels = computed(() => labelStore.filterLabelsByQuery(labels.value, query.value))
|
||||
const loading = computed(() => labelTaskService.loading || labelStore.isLoading)
|
||||
|
||||
function findLabel(newQuery: string) {
|
||||
query.value = newQuery
|
||||
|
@ -129,7 +132,8 @@ async function createAndAddLabel(title: string) {
|
|||
return
|
||||
}
|
||||
|
||||
const newLabel = await store.dispatch('labels/createLabel', new LabelModel({title}))
|
||||
const labelStore = useLabelStore()
|
||||
const newLabel = await labelStore.createLabel(new LabelModel({title}))
|
||||
addLabel(newLabel, false)
|
||||
labels.value.push(newLabel)
|
||||
success({message: t('task.label.addCreateSuccess')})
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
import {describe, it, expect} from 'vitest'
|
||||
|
||||
import {filterLabelsByQuery} from './labels'
|
||||
import {createNewIndexer} from '../indexes'
|
||||
|
||||
const {add} = createNewIndexer('labels', ['title', 'description'])
|
||||
|
||||
describe('filter labels', () => {
|
||||
const state = {
|
||||
labels: {
|
||||
1: {id: 1, title: 'label1'},
|
||||
2: {id: 2, title: 'label2'},
|
||||
3: {id: 3, title: 'label3'},
|
||||
4: {id: 4, title: 'label4'},
|
||||
5: {id: 5, title: 'label5'},
|
||||
6: {id: 6, title: 'label6'},
|
||||
7: {id: 7, title: 'label7'},
|
||||
8: {id: 8, title: 'label8'},
|
||||
9: {id: 9, title: 'label9'},
|
||||
},
|
||||
}
|
||||
|
||||
Object.values(state.labels).forEach(add)
|
||||
|
||||
it('should return an empty array for an empty query', () => {
|
||||
const labels = filterLabelsByQuery(state, [], '')
|
||||
|
||||
expect(labels).toHaveLength(0)
|
||||
})
|
||||
it('should return labels for a query', () => {
|
||||
const labels = filterLabelsByQuery(state, [], 'label2')
|
||||
|
||||
expect(labels).toHaveLength(1)
|
||||
expect(labels[0].title).toBe('label2')
|
||||
})
|
||||
it('should not return found but hidden labels', () => {
|
||||
interface label {
|
||||
id: number,
|
||||
title: string,
|
||||
}
|
||||
|
||||
const labelsToHide: label[] = [{id: 1, title: 'label1'}]
|
||||
const labels = filterLabelsByQuery(state, labelsToHide, 'label1')
|
||||
|
||||
expect(labels).toHaveLength(0)
|
||||
})
|
||||
})
|
|
@ -1,33 +0,0 @@
|
|||
import {createNewIndexer} from '../indexes'
|
||||
|
||||
import type {LabelState} from '@/store/types'
|
||||
import type {ILabel} from '@/modelTypes/ILabel'
|
||||
|
||||
const {search} = createNewIndexer('labels', ['title', 'description'])
|
||||
|
||||
/**
|
||||
* Checks if a list of labels is available in the store and filters them then query
|
||||
* @param {Object} state
|
||||
* @param {Array} labelsToHide
|
||||
* @param {String} query
|
||||
* @returns {Array}
|
||||
*/
|
||||
export function filterLabelsByQuery(state: LabelState, labelsToHide: ILabel[], query: string) {
|
||||
const labelIdsToHide: number[] = labelsToHide.map(({id}) => id)
|
||||
|
||||
return search(query)
|
||||
?.filter(value => !labelIdsToHide.includes(value))
|
||||
.map(id => state.labels[id])
|
||||
|| []
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the labels by id if found
|
||||
* @param {Object} state
|
||||
* @param {Array} ids
|
||||
* @returns {Array}
|
||||
*/
|
||||
export function getLabelsByIds(state: LabelState, ids: ILabel['id'][]) {
|
||||
return Object.values(state.labels).filter(({id}) => ids.includes(id))
|
||||
}
|
|
@ -2,6 +2,7 @@ import {createApp} from 'vue'
|
|||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
import {error, success} from './message'
|
||||
|
||||
|
@ -104,6 +105,9 @@ if (window.SENTRY_ENABLED) {
|
|||
import('./sentry').then(sentry => sentry.default(app, router))
|
||||
}
|
||||
|
||||
const pinia = createPinia()
|
||||
app.use(pinia)
|
||||
|
||||
app.use(store, key) // pass the injection key
|
||||
app.use(router)
|
||||
app.use(i18n)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type { ActionContext } from 'vuex'
|
||||
import type { StoreDefinition } from 'pinia'
|
||||
import {LOADING, LOADING_MODULE} from './mutation-types'
|
||||
import type { RootStoreState } from './types'
|
||||
|
||||
|
@ -32,3 +33,21 @@ export function setLoading<State>(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const setLoadingPinia = (store: StoreDefinition, loadFunc : ((isLoading: boolean) => void) | null = null) => {
|
||||
const timeout = setTimeout(() => {
|
||||
if (loadFunc === null) {
|
||||
store.isLoading = true
|
||||
} else {
|
||||
loadFunc(true)
|
||||
}
|
||||
}, 100)
|
||||
return () => {
|
||||
clearTimeout(timeout)
|
||||
if (loadFunc === null) {
|
||||
store.isLoading = false
|
||||
} else {
|
||||
loadFunc(false)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ import kanban from './modules/kanban'
|
|||
import tasks from './modules/tasks'
|
||||
import lists from './modules/lists'
|
||||
import attachments from './modules/attachments'
|
||||
import labels from './modules/labels'
|
||||
|
||||
import ListModel from '@/models/list'
|
||||
|
||||
|
@ -46,7 +45,6 @@ export const store = createStore<RootStoreState>({
|
|||
tasks,
|
||||
lists,
|
||||
attachments,
|
||||
labels,
|
||||
},
|
||||
state: () => ({
|
||||
loading: false,
|
||||
|
|
|
@ -1,121 +0,0 @@
|
|||
import type { Module } from 'vuex'
|
||||
|
||||
import {i18n} from '@/i18n'
|
||||
import {success} from '@/message'
|
||||
import LabelService from '@/services/label'
|
||||
import {setLoading} from '@/store/helper'
|
||||
import type { LabelState, RootStoreState } from '@/store/types'
|
||||
import {getLabelsByIds, filterLabelsByQuery} from '@/helpers/labels'
|
||||
import {createNewIndexer} from '@/indexes'
|
||||
import type { ILabel } from '@/modelTypes/ILabel'
|
||||
|
||||
const {add, remove, update} = createNewIndexer('labels', ['title', 'description'])
|
||||
|
||||
async function getAllLabels(page = 1): Promise<ILabel[]> {
|
||||
const labelService = new LabelService()
|
||||
const labels = await labelService.getAll({}, {}, page) as ILabel[]
|
||||
if (page < labelService.totalPages) {
|
||||
const nextLabels = await getAllLabels(page + 1)
|
||||
return labels.concat(nextLabels)
|
||||
} else {
|
||||
return labels
|
||||
}
|
||||
}
|
||||
|
||||
const LabelStore : Module<LabelState, RootStoreState> = {
|
||||
namespaced: true,
|
||||
state: () => ({
|
||||
labels: {},
|
||||
loaded: false,
|
||||
}),
|
||||
mutations: {
|
||||
setLabels(state, labels: ILabel[]) {
|
||||
labels.forEach(l => {
|
||||
state.labels[l.id] = l
|
||||
add(l)
|
||||
})
|
||||
},
|
||||
setLabel(state, label: ILabel) {
|
||||
state.labels[label.id] = label
|
||||
update(label)
|
||||
},
|
||||
removeLabelById(state, label: ILabel) {
|
||||
remove(label)
|
||||
delete state.labels[label.id]
|
||||
},
|
||||
setLoaded(state, loaded: boolean) {
|
||||
state.loaded = loaded
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
getLabelsByIds(state) {
|
||||
return (ids: ILabel['id'][]) => getLabelsByIds(state, ids)
|
||||
},
|
||||
filterLabelsByQuery(state) {
|
||||
return (labelsToHide: ILabel[], query: string) => filterLabelsByQuery(state, labelsToHide, query)
|
||||
},
|
||||
getLabelsByExactTitles(state) {
|
||||
return labelTitles => Object
|
||||
.values(state.labels)
|
||||
.filter(({title}) => labelTitles.some(l => l.toLowerCase() === title.toLowerCase()))
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async loadAllLabels(ctx, {forceLoad} = {}) {
|
||||
if (ctx.state.loaded && !forceLoad) {
|
||||
return
|
||||
}
|
||||
|
||||
const cancel = setLoading(ctx, 'labels')
|
||||
|
||||
try {
|
||||
const labels = await getAllLabels()
|
||||
ctx.commit('setLabels', labels)
|
||||
ctx.commit('setLoaded', true)
|
||||
return labels
|
||||
} finally {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
async deleteLabel(ctx, label: ILabel) {
|
||||
const cancel = setLoading(ctx, 'labels')
|
||||
const labelService = new LabelService()
|
||||
|
||||
try {
|
||||
const result = await labelService.delete(label)
|
||||
ctx.commit('removeLabelById', label)
|
||||
success({message: i18n.global.t('label.deleteSuccess')})
|
||||
return result
|
||||
} finally {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
async updateLabel(ctx, label: ILabel) {
|
||||
const cancel = setLoading(ctx, 'labels')
|
||||
const labelService = new LabelService()
|
||||
|
||||
try {
|
||||
const newLabel = await labelService.update(label)
|
||||
ctx.commit('setLabel', newLabel)
|
||||
success({message: i18n.global.t('label.edit.success')})
|
||||
return newLabel
|
||||
} finally {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
async createLabel(ctx, label: ILabel) {
|
||||
const cancel = setLoading(ctx, 'labels')
|
||||
const labelService = new LabelService()
|
||||
|
||||
try {
|
||||
const newLabel = await labelService.create(label)
|
||||
ctx.commit('setLabel', newLabel)
|
||||
return newLabel
|
||||
} finally {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default LabelStore
|
|
@ -25,6 +25,7 @@ import type { IAttachment } from '@/modelTypes/IAttachment'
|
|||
import type { IList } from '@/modelTypes/IList'
|
||||
|
||||
import type { RootStoreState, TaskState } from '@/store/types'
|
||||
import { useLabelStore } from '@/stores/labels'
|
||||
|
||||
// IDEA: maybe use a small fuzzy search here to prevent errors
|
||||
function findPropertyByValue(object, key, value) {
|
||||
|
@ -268,22 +269,19 @@ const tasksStore : Module<TaskState, RootStoreState>= {
|
|||
},
|
||||
|
||||
// Do everything that is involved in finding, creating and adding the label to the task
|
||||
async addLabelsToTask({rootState, dispatch}, {
|
||||
task,
|
||||
parsedLabels,
|
||||
}) {
|
||||
async addLabelsToTask(_, { task, parsedLabels }) {
|
||||
if (parsedLabels.length <= 0) {
|
||||
return task
|
||||
}
|
||||
|
||||
const {labels} = rootState.labels
|
||||
const labelStore = useLabelStore()
|
||||
|
||||
const labelAddsToWaitFor = parsedLabels.map(async labelTitle => {
|
||||
let label = validateLabel(labels, labelTitle)
|
||||
let label = validateLabel(labelStore.labels, labelTitle)
|
||||
if (typeof label === 'undefined') {
|
||||
// label not found, create it
|
||||
const labelModel = new LabelModel({title: labelTitle})
|
||||
label = await dispatch('labels/createLabel', labelModel, {root: true})
|
||||
label = await labelStore.createLabel(labelModel)
|
||||
}
|
||||
|
||||
return addLabelToTask(task, label)
|
||||
|
|
|
@ -93,7 +93,7 @@ export interface LabelState {
|
|||
labels: {
|
||||
[id: ILabel['id']]: ILabel
|
||||
},
|
||||
loaded: boolean,
|
||||
isLoading: boolean,
|
||||
}
|
||||
|
||||
export interface ListState {
|
||||
|
|
55
src/stores/labels.test.ts
Normal file
55
src/stores/labels.test.ts
Normal file
|
@ -0,0 +1,55 @@
|
|||
import {setActivePinia, createPinia} from 'pinia'
|
||||
import {describe, it, expect, beforeEach} from 'vitest'
|
||||
|
||||
import {useLabelStore} from './labels'
|
||||
|
||||
import type { ILabel } from '@/modelTypes/ILabel'
|
||||
|
||||
const MOCK_LABELS = {
|
||||
1: {id: 1, title: 'label1'},
|
||||
2: {id: 2, title: 'label2'},
|
||||
3: {id: 3, title: 'label3'},
|
||||
4: {id: 4, title: 'label4'},
|
||||
5: {id: 5, title: 'label5'},
|
||||
6: {id: 6, title: 'label6'},
|
||||
7: {id: 7, title: 'label7'},
|
||||
8: {id: 8, title: 'label8'},
|
||||
9: {id: 9, title: 'label9'},
|
||||
}
|
||||
|
||||
function setupStore() {
|
||||
const store = useLabelStore()
|
||||
store.setLabels(Object.values(MOCK_LABELS) as ILabel[])
|
||||
return store
|
||||
}
|
||||
|
||||
describe('filter labels', () => {
|
||||
beforeEach(() => {
|
||||
// creates a fresh pinia and make it active so it's automatically picked
|
||||
// up by any useStore() call without having to pass it to it:
|
||||
// `useStore(pinia)`
|
||||
setActivePinia(createPinia())
|
||||
})
|
||||
|
||||
it('should return an empty array for an empty query', () => {
|
||||
const store = setupStore()
|
||||
const labels = store.filterLabelsByQuery([], '')
|
||||
|
||||
expect(labels).toHaveLength(0)
|
||||
})
|
||||
it('should return labels for a query', () => {
|
||||
const store = setupStore()
|
||||
const labels = store.filterLabelsByQuery([], 'label2')
|
||||
|
||||
expect(labels).toHaveLength(1)
|
||||
expect(labels[0].title).toBe('label2')
|
||||
})
|
||||
it('should not return found but hidden labels', () => {
|
||||
const store = setupStore()
|
||||
|
||||
const labelsToHide = [{id: 1, title: 'label1'}] as ILabel[]
|
||||
const labels = store.filterLabelsByQuery(labelsToHide, 'label1')
|
||||
|
||||
expect(labels).toHaveLength(0)
|
||||
})
|
||||
})
|
136
src/stores/labels.ts
Normal file
136
src/stores/labels.ts
Normal file
|
@ -0,0 +1,136 @@
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
import LabelService from '@/services/label'
|
||||
import {success} from '@/message'
|
||||
import {i18n} from '@/i18n'
|
||||
import {createNewIndexer} from '@/indexes'
|
||||
import {setLoadingPinia} from '@/store/helper'
|
||||
import type {ILabel} from '@/modelTypes/ILabel'
|
||||
|
||||
const {add, remove, update, search} = createNewIndexer('labels', ['title', 'description'])
|
||||
|
||||
async function getAllLabels(page = 1): Promise<ILabel[]> {
|
||||
const labelService = new LabelService()
|
||||
const labels = await labelService.getAll({}, {}, page) as ILabel[]
|
||||
if (page < labelService.totalPages) {
|
||||
const nextLabels = await getAllLabels(page + 1)
|
||||
return labels.concat(nextLabels)
|
||||
} else {
|
||||
return labels
|
||||
}
|
||||
}
|
||||
|
||||
import type {LabelState} from '@/store/types'
|
||||
|
||||
export const useLabelStore = defineStore('label', {
|
||||
state: () : LabelState => ({
|
||||
// The labels are stored as an object which has the label ids as keys.
|
||||
labels: {},
|
||||
isLoading: false,
|
||||
}),
|
||||
|
||||
getters: {
|
||||
getLabelsByIds(state) {
|
||||
return (ids: ILabel['id'][]) => Object.values(state.labels).filter(({id}) => ids.includes(id))
|
||||
},
|
||||
// **
|
||||
// * Checks if a list of labels is available in the store and filters them then query
|
||||
// **
|
||||
filterLabelsByQuery(state) {
|
||||
return (labelsToHide: ILabel[], query: string) => {
|
||||
const labelIdsToHide: number[] = labelsToHide.map(({id}) => id)
|
||||
|
||||
return search(query)
|
||||
?.filter(value => !labelIdsToHide.includes(value))
|
||||
.map(id => state.labels[id])
|
||||
|| []
|
||||
}
|
||||
},
|
||||
getLabelsByExactTitles(state) {
|
||||
return (labelTitles: string[]) => Object
|
||||
.values(state.labels)
|
||||
.filter(({title}) => labelTitles.some(l => l.toLowerCase() === title.toLowerCase()))
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
setIsLoading(isLoading: boolean) {
|
||||
this.isLoading = isLoading
|
||||
},
|
||||
|
||||
setLabels(labels: ILabel[]) {
|
||||
labels.forEach(l => {
|
||||
this.labels[l.id] = l
|
||||
add(l)
|
||||
})
|
||||
},
|
||||
|
||||
setLabel(label: ILabel) {
|
||||
this.labels[label.id] = label
|
||||
update(label)
|
||||
},
|
||||
|
||||
removeLabelById(label: ILabel) {
|
||||
remove(label)
|
||||
delete this.labels[label.id]
|
||||
},
|
||||
|
||||
async loadAllLabels({forceLoad} : {forceLoad?: boolean} = {}) {
|
||||
if (this.isLoading && !forceLoad) {
|
||||
return
|
||||
}
|
||||
|
||||
const cancel = setLoadingPinia(useLabelStore)
|
||||
|
||||
try {
|
||||
const labels = await getAllLabels()
|
||||
this.setLabels(labels)
|
||||
this.setIsLoading(true)
|
||||
return labels
|
||||
} finally {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
|
||||
async deleteLabel(label: ILabel) {
|
||||
const cancel = setLoadingPinia(useLabelStore)
|
||||
const labelService = new LabelService()
|
||||
|
||||
try {
|
||||
const result = await labelService.delete(label)
|
||||
this.removeLabelById(label)
|
||||
success({message: i18n.global.t('label.deleteSuccess')})
|
||||
return result
|
||||
} finally {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
|
||||
async updateLabel(label: ILabel) {
|
||||
const cancel = setLoadingPinia(useLabelStore)
|
||||
const labelService = new LabelService()
|
||||
|
||||
try {
|
||||
const newLabel = await labelService.update(label)
|
||||
this.setLabel(newLabel)
|
||||
success({message: i18n.global.t('label.edit.success')})
|
||||
return newLabel
|
||||
} finally {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
|
||||
async createLabel(label: ILabel) {
|
||||
const cancel = setLoadingPinia(useLabelStore)
|
||||
const labelService = new LabelService()
|
||||
|
||||
try {
|
||||
const newLabel = await labelService.create(label)
|
||||
this.setLabel(newLabel)
|
||||
return newLabel
|
||||
} finally {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
|
@ -111,11 +111,12 @@
|
|||
|
||||
<script lang="ts">
|
||||
import {defineComponent} from 'vue'
|
||||
import {mapState} from 'vuex'
|
||||
import {mapState as mapVuexState} from 'vuex'
|
||||
import {mapState} from 'pinia'
|
||||
|
||||
import LabelModel from '@/models/label'
|
||||
import LabelModel from '../../models/label'
|
||||
import type {ILabel} from '@/modelTypes/ILabel'
|
||||
import {LOADING, LOADING_MODULE} from '@/store/mutation-types'
|
||||
import {useLabelStore} from '@/stores/labels'
|
||||
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import AsyncEditor from '@/components/input/AsyncEditor'
|
||||
|
@ -139,25 +140,32 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch('labels/loadAllLabels')
|
||||
const labelStore = useLabelStore()
|
||||
labelStore.loadAllLabels()
|
||||
},
|
||||
mounted() {
|
||||
setTitle(this.$t('label.title'))
|
||||
},
|
||||
computed: mapState({
|
||||
computed: {
|
||||
...mapVuexState({
|
||||
userInfo: state => state.auth.info,
|
||||
// Alphabetically sort the labels
|
||||
labels: state => Object.values(state.labels.labels).sort((f, s) => f.title > s.title ? 1 : -1),
|
||||
loading: state => state[LOADING] && state[LOADING_MODULE] === 'labels',
|
||||
}),
|
||||
...mapState(useLabelStore, {
|
||||
// Alphabetically sort the labels
|
||||
labels: state => Object.values(state.labels).sort((f, s) => f.title > s.title ? 1 : -1),
|
||||
loading: state => state.isLoading,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
deleteLabel(label: ILabel) {
|
||||
this.showDeleteModal = false
|
||||
this.isLabelEdit = false
|
||||
return this.$store.dispatch('labels/deleteLabel', label)
|
||||
const labelStore = useLabelStore()
|
||||
return labelStore.deleteLabel(label)
|
||||
},
|
||||
editLabelSubmit() {
|
||||
return this.$store.dispatch('labels/updateLabel', this.labelEditLabel)
|
||||
const labelStore = useLabelStore()
|
||||
return labelStore.updateLabel(this.labelEditLabel)
|
||||
},
|
||||
editLabel(label: ILabel) {
|
||||
if (label.createdBy.id !== this.userInfo.id) {
|
||||
|
|
|
@ -36,12 +36,13 @@
|
|||
|
||||
<script lang="ts">
|
||||
import {defineComponent} from 'vue'
|
||||
import {mapState} from 'pinia'
|
||||
|
||||
import LabelModel from '../../models/label'
|
||||
import CreateEdit from '@/components/misc/create-edit.vue'
|
||||
import ColorPicker from '../../components/input/colorPicker.vue'
|
||||
import {mapState} from 'vuex'
|
||||
import {LOADING, LOADING_MODULE} from '@/store/mutation-types'
|
||||
import { setTitle } from '@/helpers/setTitle'
|
||||
import { useLabelStore } from '@/stores/labels'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NewLabel',
|
||||
|
@ -58,9 +59,11 @@ export default defineComponent({
|
|||
mounted() {
|
||||
setTitle(this.$t('label.create.title'))
|
||||
},
|
||||
computed: mapState({
|
||||
loading: state => state[LOADING] && state[LOADING_MODULE] === 'labels',
|
||||
computed: {
|
||||
...mapState(useLabelStore, {
|
||||
loading: state => state.isLoading,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
async newLabel() {
|
||||
if (this.label.title === '') {
|
||||
|
@ -69,7 +72,8 @@ export default defineComponent({
|
|||
}
|
||||
this.showError = false
|
||||
|
||||
const label = this.$store.dispatch('labels/createLabel', this.label)
|
||||
const labelStore = useLabelStore()
|
||||
const label = labelStore.createLabel(this.label)
|
||||
this.$router.push({
|
||||
name: 'labels.index',
|
||||
params: {id: label.id},
|
||||
|
|
|
@ -10240,6 +10240,14 @@ pify@^4.0.1:
|
|||
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
|
||||
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
|
||||
|
||||
pinia@^2.0.21:
|
||||
version "2.0.21"
|
||||
resolved "https://registry.yarnpkg.com/pinia/-/pinia-2.0.21.tgz#2a6599ad3736fa71866f4b053ffb0073cd482270"
|
||||
integrity sha512-6ol04PtL29O0Z6JHI47O3JUSoyOJ7Og0rstXrHVMZSP4zAldsQBXJCNF0i/H7m8vp/Hjd/CSmuPl7C5QAwpeWQ==
|
||||
dependencies:
|
||||
"@vue/devtools-api" "^6.2.1"
|
||||
vue-demi "*"
|
||||
|
||||
pinkie-promise@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
|
||||
|
|
Reference in New Issue
Block a user
Shouldn't this use the setup function? (not
script setup
).We could introduce a setup block in this component.
But since this pull request was only about having the fastest way to integrate the store I didn't see that necessary.
Setup would only require the additional import of
computed
. Plus we would need to export it again from that function.So overall more complex then this quick fix here.
Makes sense, especially considering this would be redundant once we'll have everything migrated over to
script setup
.Exactly :)