// Store: localStorage-backed state. Seeded with realistic Turkish sample data. const { useState, useEffect, useMemo, useCallback, useRef } = React; const STORAGE_KEY = 'akis_pm_state_v3'; const COLUMNS = [ { id: 'todo', label: 'Yapılacak', color: '#94a3b8' }, { id: 'doing', label: 'Devam Eden', color: '#3b82f6' }, { id: 'review', label: 'İncelemede', color: '#a855f7' }, { id: 'done', label: 'Tamamlandı', color: '#10b981' }, ]; const TAG_COLORS = ['emerald','blue','purple','pink','amber','red','gray','teal']; const today = new Date(); const toDateKey = (date) => { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; }; const parseDateKey = (dateStr) => { if (!dateStr) return null; const [year, month, day] = dateStr.split('-').map(Number); return new Date(year, month - 1, day); }; const d = (offset) => { const dt = new Date(today); dt.setDate(dt.getDate() + offset); return toDateKey(dt); }; const uid = () => Math.random().toString(36).slice(2, 10); const SEED = { theme: 'light', activeProjectId: null, activeView: 'dashboard', projects: [], tags: [], tasks: [], }; function loadState() { try { const raw = localStorage.getItem(STORAGE_KEY); if (raw) return JSON.parse(raw); } catch (e) { console.warn('localStorage load failed', e); } return SEED; } function normalizeState(data = {}) { return { ...SEED, ...data, projects: Array.isArray(data.projects) ? data.projects : [], tags: Array.isArray(data.tags) ? data.tags : [], tasks: Array.isArray(data.tasks) ? data.tasks : [], }; } function saveState(s) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(s)); } catch (e) { console.warn('localStorage save failed', e); } } function useStore() { const [state, setState] = useState(loadState); useEffect(() => { saveState(state); }, [state]); // Apply theme to useEffect(() => { document.documentElement.dataset.theme = state.theme; }, [state.theme]); const api = useMemo(() => ({ replaceState: (nextState) => setState(s => normalizeState({ ...s, ...nextState })), setTheme: (theme) => setState(s => ({ ...s, theme })), setActiveProject: (id) => setState(s => ({ ...s, activeProjectId: id })), setActiveView: (v) => setState(s => ({ ...s, activeView: v })), addProject: (data) => setState(s => ({ ...s, projects: [...s.projects, { id: uid(), color: '#047857', description: '', ...data }], })), updateProject: (id, patch) => setState(s => ({ ...s, projects: s.projects.map(p => p.id === id ? { ...p, ...patch } : p), })), deleteProject: (id) => setState(s => ({ ...s, projects: s.projects.filter(p => p.id !== id), tasks: s.tasks.filter(t => t.projectId !== id), activeProjectId: s.activeProjectId === id ? (s.projects.find(p => p.id !== id)?.id || null) : s.activeProjectId, })), addTask: (data) => setState(s => { const newTask = { id: uid(), projectId: data.projectId || s.activeProjectId, title: data.title || 'Yeni görev', status: data.status || 'todo', priority: data.priority || 'medium', due: data.due || null, tags: data.tags || [], subtasks: data.subtasks || [], description: data.description || '', createdAt: new Date().toISOString(), }; return { ...s, tasks: [...s.tasks, newTask] }; }), updateTask: (id, patch) => setState(s => ({ ...s, tasks: s.tasks.map(t => t.id === id ? { ...t, ...patch } : t), })), deleteTask: (id) => setState(s => ({ ...s, tasks: s.tasks.filter(t => t.id !== id) })), toggleTaskDone: (id) => setState(s => ({ ...s, tasks: s.tasks.map(t => t.id === id ? { ...t, status: t.status === 'done' ? 'todo' : 'done' } : t), })), addSubtask: (taskId, title) => setState(s => ({ ...s, tasks: s.tasks.map(t => t.id === taskId ? { ...t, subtasks: [...(t.subtasks||[]), { id: uid(), title, done: false }] } : t), })), updateSubtask: (taskId, subId, patch) => setState(s => ({ ...s, tasks: s.tasks.map(t => t.id === taskId ? { ...t, subtasks: t.subtasks.map(st => st.id === subId ? { ...st, ...patch } : st) } : t), })), removeSubtask: (taskId, subId) => setState(s => ({ ...s, tasks: s.tasks.map(t => t.id === taskId ? { ...t, subtasks: t.subtasks.filter(st => st.id !== subId) } : t), })), addTag: (data) => setState(s => ({ ...s, tags: [...s.tags, { id: uid(), color: 'gray', ...data }] })), resetData: () => { localStorage.removeItem(STORAGE_KEY); setState(SEED); }, }), []); return { state, ...api }; } // === Derived helpers === function taskProgress(task) { if (!task.subtasks || task.subtasks.length === 0) { return task.status === 'done' ? 100 : (task.status === 'review' ? 80 : (task.status === 'doing' ? 40 : 0)); } const done = task.subtasks.filter(s => s.done).length; return Math.round((done / task.subtasks.length) * 100); } function projectProgress(projectId, tasks) { const ts = tasks.filter(t => t.projectId === projectId); if (ts.length === 0) return 0; const done = ts.filter(t => t.status === 'done').length; return Math.round((done / ts.length) * 100); } function formatDate(dateStr) { if (!dateStr) return ''; const dt = parseDateKey(dateStr); const months = ['Oca','Şub','Mar','Nis','May','Haz','Tem','Ağu','Eyl','Eki','Kas','Ara']; return `${dt.getDate()} ${months[dt.getMonth()]}`; } function relativeDate(dateStr) { if (!dateStr) return ''; const dt = parseDateKey(dateStr); dt.setHours(0,0,0,0); const now = new Date(); now.setHours(0,0,0,0); const diff = Math.round((dt - now) / (1000 * 60 * 60 * 24)); if (diff === 0) return 'Bugün'; if (diff === 1) return 'Yarın'; if (diff === -1) return 'Dün'; if (diff < 0) return `${-diff} gün gecikti`; if (diff < 7) return `${diff} gün kaldı`; return formatDate(dateStr); } function isOverdue(dateStr, status) { if (!dateStr || status === 'done') return false; const dt = parseDateKey(dateStr); dt.setHours(0,0,0,0); const now = new Date(); now.setHours(0,0,0,0); return dt < now; } window.PMStore = { useStore, COLUMNS, TAG_COLORS, taskProgress, projectProgress, formatDate, relativeDate, isOverdue, uid, toDateKey, parseDateKey, normalizeState };