diff --git a/web/src/stores/label.test.ts b/web/src/stores/label.test.ts new file mode 100644 index 0000000..63ae543 --- /dev/null +++ b/web/src/stores/label.test.ts @@ -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() + }) +}) diff --git a/web/src/stores/label.ts b/web/src/stores/label.ts new file mode 100644 index 0000000..f5b66f5 --- /dev/null +++ b/web/src/stores/label.ts @@ -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([]) + const totalRemaining = ref(0) + const lastAction = ref(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, + } +})