feat(avocet): Pinia label store with queue, lastAction, easter egg counter
This commit is contained in:
parent
2abda18b92
commit
209f49f7ea
2 changed files with 115 additions and 0 deletions
62
web/src/stores/label.test.ts
Normal file
62
web/src/stores/label.test.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// src/stores/label.test.ts
|
||||
import { setActivePinia, createPinia } from 'pinia'
|
||||
import { useLabelStore } from './label'
|
||||
import { beforeEach, describe, it, expect } from 'vitest'
|
||||
|
||||
const MOCK_ITEM = {
|
||||
id: 'abc', subject: 'Test', body: 'Body', from: 'a@b.com',
|
||||
date: '2026-03-01', source: 'imap:test',
|
||||
}
|
||||
|
||||
describe('label store', () => {
|
||||
beforeEach(() => setActivePinia(createPinia()))
|
||||
|
||||
it('starts with empty queue', () => {
|
||||
const store = useLabelStore()
|
||||
expect(store.queue).toEqual([])
|
||||
expect(store.current).toBeNull()
|
||||
})
|
||||
|
||||
it('current returns first item', () => {
|
||||
const store = useLabelStore()
|
||||
store.queue = [MOCK_ITEM]
|
||||
expect(store.current).toEqual(MOCK_ITEM)
|
||||
})
|
||||
|
||||
it('removeCurrentFromQueue removes first item', () => {
|
||||
const store = useLabelStore()
|
||||
store.queue = [MOCK_ITEM, { ...MOCK_ITEM, id: 'def' }]
|
||||
store.removeCurrentFromQueue()
|
||||
expect(store.queue[0].id).toBe('def')
|
||||
})
|
||||
|
||||
it('tracks lastAction', () => {
|
||||
const store = useLabelStore()
|
||||
store.queue = [MOCK_ITEM]
|
||||
store.setLastAction('label', MOCK_ITEM, 'interview_scheduled')
|
||||
expect(store.lastAction?.type).toBe('label')
|
||||
expect(store.lastAction?.label).toBe('interview_scheduled')
|
||||
})
|
||||
|
||||
it('incrementLabeled increases sessionLabeled', () => {
|
||||
const store = useLabelStore()
|
||||
store.incrementLabeled()
|
||||
store.incrementLabeled()
|
||||
expect(store.sessionLabeled).toBe(2)
|
||||
})
|
||||
|
||||
it('restoreItem adds to front of queue', () => {
|
||||
const store = useLabelStore()
|
||||
store.queue = [{ ...MOCK_ITEM, id: 'def' }]
|
||||
store.restoreItem(MOCK_ITEM)
|
||||
expect(store.queue[0].id).toBe('abc')
|
||||
expect(store.queue[1].id).toBe('def')
|
||||
})
|
||||
|
||||
it('clearLastAction nulls lastAction', () => {
|
||||
const store = useLabelStore()
|
||||
store.setLastAction('skip', MOCK_ITEM)
|
||||
store.clearLastAction()
|
||||
expect(store.lastAction).toBeNull()
|
||||
})
|
||||
})
|
||||
53
web/src/stores/label.ts
Normal file
53
web/src/stores/label.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// src/stores/label.ts
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
export interface QueueItem {
|
||||
id: string
|
||||
subject: string
|
||||
body: string
|
||||
from: string
|
||||
date: string
|
||||
source: string
|
||||
}
|
||||
|
||||
export interface LastAction {
|
||||
type: 'label' | 'skip' | 'discard'
|
||||
item: QueueItem
|
||||
label?: string
|
||||
}
|
||||
|
||||
export const useLabelStore = defineStore('label', () => {
|
||||
const queue = ref<QueueItem[]>([])
|
||||
const totalRemaining = ref(0)
|
||||
const lastAction = ref<LastAction | null>(null)
|
||||
const sessionLabeled = ref(0) // for easter eggs
|
||||
|
||||
const current = computed(() => queue.value[0] ?? null)
|
||||
|
||||
function removeCurrentFromQueue() {
|
||||
queue.value.shift()
|
||||
}
|
||||
|
||||
function setLastAction(type: LastAction['type'], item: QueueItem, label?: string) {
|
||||
lastAction.value = { type, item, label }
|
||||
}
|
||||
|
||||
function clearLastAction() {
|
||||
lastAction.value = null
|
||||
}
|
||||
|
||||
function restoreItem(item: QueueItem) {
|
||||
queue.value.unshift(item)
|
||||
}
|
||||
|
||||
function incrementLabeled() {
|
||||
sessionLabeled.value++
|
||||
}
|
||||
|
||||
return {
|
||||
queue, totalRemaining, lastAction, sessionLabeled, current,
|
||||
removeCurrentFromQueue, setLastAction, clearLastAction,
|
||||
restoreItem, incrementLabeled,
|
||||
}
|
||||
})
|
||||
Loading…
Reference in a new issue