diff --git a/src/app/dashboard/quick-capture/page.tsx b/src/app/dashboard/quick-capture/page.tsx new file mode 100644 index 0000000..833010d --- /dev/null +++ b/src/app/dashboard/quick-capture/page.tsx @@ -0,0 +1,134 @@ +'use client'; + +import { useState } from 'react'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { apiFetch } from '../../../lib/api'; +import { useSession } from '../../../components/session-provider'; + +type CaptureType = 'task' | 'decision' | 'observation' | 'goal' | 'note'; + +const TYPES: { id: CaptureType; label: string; icon: string; placeholder: string }[] = [ + { id: 'task', label: 'Task', icon: '✓', placeholder: 'Ce trebuie făcut? (ex: Trimite oferta clientului X)' }, + { id: 'decision', label: 'Decizie', icon: '⚡', placeholder: 'Ce ai decis? (ex: Mergem pe stack-ul TypeScript)' }, + { id: 'observation', label: 'Observație', icon: '📊', placeholder: 'Ce ai observat/măsurat? (metric: valoare)' }, + { id: 'goal', label: 'Obiectiv', icon: '🎯', placeholder: 'Ce vrei să atingi? (ex: Lansarea MVP-ului până în oct)' }, + { id: 'note', label: 'Notă', icon: '📝', placeholder: 'Orice altceva important…' }, +]; + +export default function QuickCapturePage() { + const { activeTenant } = useSession(); + const tenantId = activeTenant?.tenantId ?? ''; + const qc = useQueryClient(); + const [type, setType] = useState('task'); + const [text, setText] = useState(''); + const [priority, setPriority] = useState('normal'); + const [tag, setTag] = useState(''); + const [saved, setSaved] = useState([]); + + const mut = useMutation({ + mutationFn: () => { + const tags = tag.trim() ? [tag.trim()] : []; + const trimmed = text.trim(); + if (!trimmed) throw new Error('empty'); + + if (type === 'task') { + return apiFetch('/v1/tasks', { tenantId, method: 'POST', body: { title: trimmed, priority, tags, status: 'todo' } }); + } + if (type === 'decision') { + return apiFetch('/v1/decisions', { tenantId, method: 'POST', body: { title: trimmed, impact: priority === 'urgent' ? 'high' : 'medium', tags } }); + } + if (type === 'observation') { + const [metric, ...rest] = trimmed.includes(':') ? trimmed.split(':') : ['nota', [trimmed]]; + return apiFetch('/v1/observations', { tenantId, method: 'POST', body: { + metric: metric.trim(), value: (rest.join(':') || trimmed).trim(), + subjectType: 'personal', confidence: 0.9, observedAt: new Date().toISOString(), + }}); + } + if (type === 'goal') { + return apiFetch('/v1/goals', { tenantId, method: 'POST', body: { title: trimmed, status: 'active', progress: 0, tags } }); + } + // note → task with tag=note + return apiFetch('/v1/tasks', { tenantId, method: 'POST', body: { title: trimmed, tags: ['note', ...tags], status: 'todo' } }); + }, + onSuccess: () => { + setSaved((prev) => [`${type}: ${text.trim().slice(0, 60)}`, ...prev.slice(0, 9)]); + setText(''); + setTag(''); + qc.invalidateQueries({ queryKey: ['tasks', tenantId] }); + qc.invalidateQueries({ queryKey: ['decisions', tenantId] }); + }, + }); + + const currentType = TYPES.find((t) => t.id === type) ?? TYPES[0]; + + function handleKey(e: React.KeyboardEvent) { + if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); if (text.trim()) mut.mutate(); } + } + + return ( +
+
+

Quick Capture

+

Captează rapid idei, taskuri, decizii și observații. Ctrl+Enter pentru a salva.

+
+ + {/* Type selector */} +
+ {TYPES.map((t) => ( + + ))} +
+ + {/* Input area */} +
+